Obsolete Magellan serial and SD formats (#961)
authorRobert Lipe <robertlipe@users.noreply.github.com>
Wed, 28 Dec 2022 22:37:07 +0000 (16:37 -0600)
committerGitHub <noreply@github.com>
Wed, 28 Dec 2022 22:37:07 +0000 (16:37 -0600)
* Remove Magellan serial and SD formats. Two different polls (one on the list, one in groundspeak forums) have confirmed disuse.

38 files changed:
CMakeLists.txt
deprecated/explorist_ini.cc [new file with mode: 0644]
deprecated/explorist_ini.h [new file with mode: 0644]
deprecated/magellan.h [new file with mode: 0644]
deprecated/magproto.cc [new file with mode: 0644]
explorist_ini.cc [deleted file]
explorist_ini.h [deleted file]
magellan.h [deleted file]
magproto.cc [deleted file]
reference/dusky.gnuplot
reference/dusky.gpx [new file with mode: 0644]
reference/format0.txt
reference/format1.txt
reference/format2.txt
reference/format3.txt
reference/help.txt
reference/magfile [deleted file]
reference/magxfile [deleted file]
reference/track/meridian.trk [deleted file]
testo.d/classic-1.test [deleted file]
testo.d/classic-2.test
testo.d/magellan.test [deleted file]
testo.d/magellan_sd.test [deleted file]
vecs.cc
xmldoc/chapters/preface.xml
xmldoc/chapters/use.xml
xmldoc/formats/magellan.xml [deleted file]
xmldoc/formats/magellan1.xml [deleted file]
xmldoc/formats/magellanx.xml [deleted file]
xmldoc/formats/options/magellan-deficon.xml [deleted file]
xmldoc/formats/options/magellan-maxcmts.xml [deleted file]
xmldoc/formats/options/magellan1-baud.xml [deleted file]
xmldoc/formats/options/magellan1-deficon.xml [deleted file]
xmldoc/formats/options/magellan1-maxcmts.xml [deleted file]
xmldoc/formats/options/magellan1-noack.xml [deleted file]
xmldoc/formats/options/magellan1-nukewpt.xml [deleted file]
xmldoc/formats/options/magellanx-deficon.xml [deleted file]
xmldoc/formats/options/magellanx-maxcmts.xml [deleted file]

index caecf0c762209335cfbed60a1bc14d29ea4887c9..6a985892ded735907862267c9453827be6618be5 100644 (file)
@@ -67,14 +67,12 @@ set(RESOURCES gpsbabel.qrc)
 
 # MINIMAL_FMTS
 set(MINIMAL_FMTS
-  explorist_ini.cc
   garmin.cc
   garmin_device_xml.cc
   garmin_tables.cc
   geo.cc
   gpx.cc
   kml.cc
-  magproto.cc
   nmea.cc
   wbt-200.cc
 )
@@ -209,7 +207,6 @@ set(HEADERS
   defs.h
   dg-100.h
   exif.h
-  explorist_ini.h
   filter.h
   filter_vecs.h
   format.h
@@ -237,7 +234,6 @@ set(HEADERS
   kml.h
   legacyformat.h
   lowranceusr.h
-  magellan.h
   navilink.h
   nmea.h
   osm.h
@@ -376,7 +372,6 @@ set(TESTS
   arc
   batch
   bend
-  classic-1
   classic-2
   classic-3
   dg100
@@ -408,8 +403,6 @@ set(TESTS
   kml-read
   kml
   lowranceusr
-  magellan_sd
-  magellan
   mtk
   multiurlgpx
   navilink
diff --git a/deprecated/explorist_ini.cc b/deprecated/explorist_ini.cc
new file mode 100644 (file)
index 0000000..e9eafdb
--- /dev/null
@@ -0,0 +1,74 @@
+#include "defs.h"
+#include "explorist_ini.h"
+#include "inifile.h"
+
+static inifile_t* inifile;
+static const char myname[] = "explorist";
+
+#ifdef DEAD_CODE_IS_REBORN
+static const char*
+explorist_read_value(const char* section, const char* key)
+{
+  return inifile_readstr(inifile, section, key);
+}
+#endif
+
+static mag_info*
+explorist_ini_try(const char* path)
+{
+  char* inipath;
+
+  xasprintf(&inipath, "%s/%s", path, "APP/Atlas.ini");
+  inifile = inifile_init(QString::fromUtf8(inipath), myname);
+  if (!inifile) {
+    xfree(inipath);
+    return nullptr;
+  }
+
+  auto* info = (mag_info*) xmalloc(sizeof(mag_info));
+  info->geo_path = nullptr;
+  info->track_path = nullptr;
+  info->waypoint_path = nullptr;
+
+  QString s = inifile_readstr(inifile,  "UGDS", "WpFolder");
+  if (!s.isNull()) {
+    s.replace('\\', '/');
+    xasprintf(&info->waypoint_path, "%s/%s", path, CSTR(s));
+  }
+  s = inifile_readstr(inifile,  "UGDS", "GcFolder");
+  if (!s.isNull()) {
+    s.replace('\\', '/');
+    xasprintf(&info->geo_path, "%s/%s", path, CSTR(s));
+  }
+  s = inifile_readstr(inifile,  "UGDS", "TrkFolder");
+  if (!s.isNull()) {
+    s.replace('\\', '/');
+    xasprintf(&info->track_path, "%s/%s", path, CSTR(s));
+  }
+
+  inifile_done(inifile);
+  xfree(inipath);
+  return info;
+}
+
+mag_info*
+explorist_ini_get(const char** dirlist)
+{
+  mag_info* r = nullptr;
+  while (dirlist && *dirlist) {
+    r = explorist_ini_try(*dirlist);
+    if (r) {
+      return r;
+    }
+  }
+  return r;
+}
+
+void
+explorist_ini_done(mag_info* info)
+{
+  xfree(info->geo_path);
+  xfree(info->track_path);
+  xfree(info->waypoint_path);
+  xfree(info);
+}
diff --git a/deprecated/explorist_ini.h b/deprecated/explorist_ini.h
new file mode 100644 (file)
index 0000000..078c0dc
--- /dev/null
@@ -0,0 +1,12 @@
+
+/*
+ * Interesting traits of the device from the *.ini files.
+ */
+struct mag_info {
+  char* geo_path;
+  char* track_path;
+  char* waypoint_path;
+};
+
+mag_info* explorist_ini_get(const char** directory_list);
+void explorist_ini_done(mag_info* info);
diff --git a/deprecated/magellan.h b/deprecated/magellan.h
new file mode 100644 (file)
index 0000000..67f0626
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    Copyright (C) 2002-2005 Robert Lipe, robertlipe@usa.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+ */
+
+#ifndef MAGELLAN_H_INCLUDED_
+#define MAGELLAN_H_INCLUDED_
+
+#include <QString>
+#include "defs.h"
+
+/*
+ * Table of "interesting" Magellan models.
+ * Selfishly, if I haven't heard of it, it's not in the table.
+ * This doesn't mean I actually have TRIED all models listed below.
+ * (Donations welcome. :-)
+ */
+enum meridian_model {
+  mm_unknown = 0 ,
+  mm_gps315320,
+  mm_map410,
+  mm_map330,
+  mm_gps310,
+  mm_meridian,
+  mm_sportrak
+};
+
+struct pid_to_model_t {
+  meridian_model model;
+  int pid;
+  const char* model_n;
+};
+
+struct magellan_icon_mapping_t {
+  const char* token;
+  const char* icon;
+};
+
+QString mag_find_descr_from_token(const char* token);
+QString mag_find_token_from_descr(const QString& icon);
+
+unsigned int mag_checksum(const char*buf);
+QString m330_cleanse(const char* istring);
+
+Waypoint* mag_trkparse(char* trkmsg);
+void mag_rteparse(char* rtemsg);
+
+#endif  // MAGELLAN_H_INCLUDED_
diff --git a/deprecated/magproto.cc b/deprecated/magproto.cc
new file mode 100644 (file)
index 0000000..b52beca
--- /dev/null
@@ -0,0 +1,1631 @@
+/*
+    Communicate Thales/Magellan serial protocol.
+
+    Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007,
+      2008, 2010  Robert Lipe, robertlipe+source@gpsbabel.org
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+ */
+
+#include <cassert>                 // for assert
+#include <cctype>                  // for isprint, toupper
+#include <cmath>                   // for fabs, lround
+#include <cstdio>                  // for sscanf, size_t
+#include <cstdlib>                 // for strtod, strtoul
+#include <cstring>                 // for strchr, strncmp, strlen, memmove, strrchr, memset
+
+#include <QByteArray>              // for QByteArray
+#include <QDateTime>               // for QDateTime
+#include <QDir>                    // for QDir, operator|, QDir::Files, QDir::Name, QDir::Readable
+#include <QFileInfo>               // for QFileInfo
+#include <QFileInfoList>           // for QFileInfoList
+#include <QLatin1String>           // for QLatin1String
+#include <QList>                   // for QList
+#include <QScopedPointer>          // for QScopedPointer
+#include <QString>                 // for QString, operator==
+#include <QStringList>             // for QStringList
+#include <QTime>                   // for QTime
+#include <QVector>                 // for QVector
+#include <Qt>                      // for CaseInsensitive
+#include <QtGlobal>                // for qPrintable, foreach
+
+#include "defs.h"
+#include "explorist_ini.h"         // for explorist_ini_done, explorist_ini_get, mag_info
+#include "format.h"                // for Format
+#include "gbfile.h"                // for gbfclose, gbfeof, gbfgets, gbfopen, gbfwrite, gbfile
+#include "gbser.h"                 // for gbser_deinit, gbser_init, gbser_is_serial, gbser_read_line, gbser_set_port, gbser_write, gbser_OK
+#include "magellan.h"              // for mm_meridian, mm_sportrak, magellan_icon_mapping_t, mm_gps315320, mm_unknown, mm_map330, mm_map410, pid_to_model_t, mm_gps310, m330_cleanse, mag_checksum, mag_find_descr_from_token, mag_find_token_from_descr, mag_rteparse, mag_trkparse
+#include "src/core/datetime.h"     // for DateTime
+#include "vecs.h"                  // for Vecs
+
+
+static int bitrate = 4800;
+static int wptcmtcnt;
+static int wptcmtcnt_max;
+static int explorist;
+static int broken_sportrak;
+#define MYNAME "MAGPROTO"
+#define MAXCMTCT 200
+
+#define debug_serial  (global_opts.debug_level > 1)
+
+static QString termread(char* ibuf, int size);
+static void termwrite(const char* obuf, int size);
+static void mag_readmsg(gpsdata_type objective);
+static void mag_handon();
+static void mag_handoff();
+static short_handle mkshort_handle = nullptr;
+static char* deficon = nullptr;
+static char* bs = nullptr;
+static char* cmts = nullptr;
+static char* noack = nullptr;
+static char* nukewpt = nullptr;
+static int route_out_count;
+static int waypoint_read_count;
+static int wpt_len = 8;
+static QString curfname;
+static int extension_hint;
+// For Explorist GC/510/610/710 families, bludgeon in GPX support.
+// (This has nothing to do with the Explorist 100...600 products.)
+static Format* gpx_vec;
+static mag_info* explorist_info;
+static QStringList os_gpx_files(const char* dirname);
+
+/*
+ * Magellan's firmware is *horribly* slow to send the next packet after
+ * we turn around an ack while we are reading from the device.  It's
+ * quite spiffy when we're writing to the device.   Since we're *way*
+ * less likely to lose data while reading from it than it is to lose data
+ * when we write to it, we turn off the acks when we are predominantly
+ * reading.
+ */
+static int suppress_ack;
+
+enum mag_rxstate {
+  mrs_handoff = 0,
+  mrs_handon,
+  mrs_awaiting_ack
+};
+
+/*
+ *   An individual element of a route.
+ */
+struct mag_rte_elem {
+  QString wpt_name;
+  QString wpt_icon;
+};
+
+/*
+ *  A header of a route.  Related elements of a route belong to this.
+ */
+struct mag_rte_head_t {
+  QList<mag_rte_elem*> elem_list; /* list of child rte_elems */
+  char* rte_name{nullptr};
+  int nelems{0};
+};
+
+static QList<Waypoint*> rte_wpt_tmp; /* temporary PGMNWPL msgs for routes */
+
+static gbfile* magfile_h;
+static mag_rxstate magrxstate;
+static int mag_error;
+static unsigned int last_rx_csum;
+static int found_done;
+static int got_version;
+static int is_file = 0;
+static route_head* trk_head;
+static int ignore_unable;
+
+static Waypoint* mag_wptparse(char*);
+using cleanse_fn = QString (const char*);
+static cleanse_fn* mag_cleanse;
+static const char** os_get_magellan_mountpoints();
+
+static const magellan_icon_mapping_t gps315_icon_table[] = {
+  { "a", "filled circle" },
+  { "b", "box" },
+  { "c", "red buoy" },
+  { "d", "green buoy" },
+  { "e", "buoy" },
+  { "f", "rocks" },
+  { "g", "red daymark" },
+  { "h", "green daymark" },
+  { "i", "bell" },
+  { "j", "danger" },
+  { "k", "diver down" },
+  { "l", "fish" },
+  { "m", "house" },
+  { "n", "mark" },
+  { "o", "car" },
+  { "p", "tent" },
+  { "q", "boat" },
+  { "r", "food" },
+  { "s", "fuel" },
+  { "t", "tree" },
+  { nullptr, nullptr }
+};
+
+static const magellan_icon_mapping_t map330_icon_table[] = {
+  { "a", "crossed square" },
+  { "b", "box" },
+  { "c", "house" },
+  { "d", "aerial" },
+  { "e", "airport" },
+  { "f", "amusement park" },
+  { "g", "ATM" },
+  { "g", "Bank" },
+  { "h", "auto repair" },
+  { "i", "boating" },
+  { "j", "camping" },
+  { "k", "exit ramp" },
+  { "l", "first aid" },
+  { "m", "nav aid" },
+  { "n", "buoy" },
+  { "o", "fuel" },
+  { "p", "garden" },
+  { "q", "golf" },
+  { "r", "hotel" },
+  { "s", "hunting/fishing" },
+  { "t", "large city" },
+  { "u", "lighthouse" },
+  { "v", "major city" },
+  { "w", "marina" },
+  { "x", "medium city" },
+  { "y", "museum" },
+  { "z", "obstruction" },
+  { "aa", "park" },
+  { "ab", "resort" },
+  { "ac", "restaurant" },
+  { "ad", "rock" },
+  { "ae", "scuba" },
+  { "af", "RV service" },
+  { "ag", "shooting" },
+  { "ah", "sight seeing" },
+  { "ai", "small city" },
+  { "aj", "sounding" },
+  { "ak", "sports arena" },
+  { "al", "tourist info" },
+  { "am", "truck service" },
+  { "an", "winery" },
+  { "ao", "wreck" },
+  { "ap", "zoo" },
+  { "ah", "Virtual cache"},    /* Binos: because you "see" them. */
+  { "ak", "Micro-Cache" },     /* Looks like a film canister. */
+  { "an", "Multi-Cache"},      /* Winery: grapes 'coz they "bunch" */
+  { "s",  "Unknown Cache"},    /* 'Surprise' cache: use a target. */
+  { "ac",  "Event Cache"},     /* Event caches.  May be food. */
+  { nullptr, nullptr }
+};
+
+pid_to_model_t pid_to_model[] = {
+  { mm_gps315320, 19, "ColorTrak" },
+  { mm_gps315320, 24, "GPS 315/320" },
+  { mm_map410, 25, "Map 410" },
+  { mm_map330, 30, "Map 330" },
+  { mm_gps310, 31, "GPS 310" },
+  { mm_meridian, 33, "Meridian" },
+  { mm_meridian, 35, "ProMark 2" },
+  { mm_sportrak, 36, "SporTrak Map/Pro" },
+  { mm_sportrak, 37, "SporTrak" },
+  { mm_meridian, 38, "FX324 Plotter" },
+  { mm_meridian, 39, "Meridian Color" },
+  { mm_meridian, 40, "FX324C Plotter" },
+  { mm_sportrak, 41, "Sportrak Color" },
+  { mm_sportrak, 42, "Sportrak Marine" },
+  { mm_meridian, 43, "Meridian Marine" },
+  { mm_sportrak, 44, "Sportrak Topo" },
+  { mm_sportrak, 45, "Mystic" },
+  { mm_meridian, 46, "MobileMapper" },
+  { mm_meridian, 110, "Explorist 100" },
+  { mm_meridian, 111, "Explorist 200" },
+  { mm_unknown, 0, nullptr }
+};
+
+static const magellan_icon_mapping_t* icon_mapping = map330_icon_table;
+
+/*
+ *   For each receiver type, return a "cleansed" version of the string
+ *   that's valid for a waypoint name or comment.   The string should be
+ *   freed when you're done with it.
+ */
+static QString
+m315_cleanse(const char* istring)
+{
+  char* rstring = (char*) xmalloc(strlen(istring)+1);
+  char* o;
+  const char* i;
+  static char m315_valid_chars[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789";
+  for (o=rstring,i=istring; *i; i++) {
+    if (strchr(m315_valid_chars, toupper(*i))) {
+      *o++ = toupper(*i);
+    }
+  }
+  *o = 0;
+  QString rv(rstring);
+  xfree(rstring);
+  return rv;
+}
+
+/*
+ * Do same for 330, Meridian, and SportTrak.
+ */
+QString
+m330_cleanse(const char* istring)
+{
+  static char m330_valid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
+                                   "abcdefghijklmnopqrstuvwxyz"
+                                   "0123456789+-.'/!@#<%^&>()=:\\";
+  char* rstring = (char*) xmalloc(strlen(istring)+1);
+  char* o;
+  const char* i;
+
+  for (o=rstring,i=istring; *i; i++) {
+    if (strchr(m330_valid_chars, *i)) {
+      *o++ = *i;
+    }
+  }
+  *o = 0;
+  QString rv(rstring);
+  xfree(rstring);
+  return rv;
+}
+
+/*
+ * Given a protocol message, compute the checksum as needed by
+ * the Magellan protocol.
+ */
+unsigned int
+mag_checksum(const char* const buf)
+{
+  int csum = 0;
+
+  for (const char* p = buf; *p; p++) {
+    csum  ^= *p;
+  }
+
+  return csum;
+}
+static unsigned int
+mag_pchecksum(const char* const buf, int len)
+{
+  int csum = 0;
+  const char* p = buf;
+  for (; len ; len--) {
+    csum ^= *p++;
+  }
+  return csum;
+}
+
+static void
+mag_writemsg(const char* const buf)
+{
+  unsigned int osum = mag_checksum(buf);
+  int retry_cnt = 5;
+  QScopedPointer<char, QScopedPointerPodDeleter> obuf;
+
+  if (debug_serial) {
+    warning("WRITE: $%s*%02X\r\n",buf, osum);
+  }
+
+retry:
+
+  int i = xasprintf(obuf, "$%s*%02X\r\n",buf, osum);
+  termwrite(obuf.data(), i);
+  if (magrxstate == mrs_handon || magrxstate == mrs_awaiting_ack) {
+    magrxstate = mrs_awaiting_ack;
+    mag_readmsg(trkdata);
+    if (last_rx_csum != osum) {
+      if (debug_serial) {
+        warning("COMM ERROR: Expected %02x, got %02x",
+                osum, last_rx_csum);
+      }
+      if (retry_cnt--) {
+        goto retry;
+      } else {
+        mag_handoff();
+        fatal(MYNAME
+              ": Too many communication errors.\n");
+      }
+    }
+  }
+}
+
+static void
+mag_writeack(int osum)
+{
+  QScopedPointer<char, QScopedPointerPodDeleter> nbuf;
+  QScopedPointer<char, QScopedPointerPodDeleter> obuf;
+
+  if (is_file) {
+    return;
+  }
+
+  (void) xasprintf(nbuf, "PMGNCSM,%02X", osum);
+  unsigned int nsum = mag_checksum(nbuf.data());
+  int i = xasprintf(obuf, "$%s*%02X\r\n",nbuf.data(), nsum);
+
+  if (debug_serial) {
+    warning("ACK WRITE: %s",obuf.data());
+  }
+  /*
+   * Don't call mag_writemsg here so we don't get into ack feedback
+   * loops.
+   */
+  termwrite(obuf.data(), i);
+}
+
+static void
+mag_handon()
+{
+  if (!is_file) {
+    mag_writemsg("PMGNCMD,HANDON");
+  }
+  magrxstate = mrs_handon;
+
+}
+
+static void
+mag_handoff()
+{
+  if (!is_file) {
+    mag_writemsg("PMGNCMD,HANDOFF");
+  }
+  magrxstate = mrs_handoff;
+}
+
+static void
+mag_verparse(char* ibuf)
+{
+  int prodid = mm_unknown;
+  char version[1024];
+  pid_to_model_t* pp = pid_to_model;
+
+  got_version = 1;
+  sscanf(ibuf,"$PMGNVER,%d,%[^,]", &prodid, version);
+
+  for (pp = pid_to_model; pp->model != mm_unknown; pp++) {
+    if (pp->pid == prodid) {
+      break;
+    }
+  }
+
+  if (prodid == 37) {
+    broken_sportrak = 1;
+  }
+
+  switch (pp->model) {
+  case mm_gps315320:
+  case mm_map410:
+    icon_mapping = gps315_icon_table;
+    setshort_length(mkshort_handle, 6);
+    setshort_mustupper(mkshort_handle, 1);
+    mag_cleanse = m315_cleanse;
+    break;
+  case mm_map330:
+  case mm_meridian:
+  case mm_sportrak:
+    icon_mapping = map330_icon_table;
+    setshort_length(mkshort_handle, wpt_len);
+    setshort_mustupper(mkshort_handle, 0);
+    mag_cleanse = m330_cleanse;
+    break;
+  default:
+    fatal(MYNAME ": Unknown receiver type %d, model version '%s'.\n", prodid, version);
+  }
+}
+
+#define IS_TKN(x) (strncmp(ibuf,x, sizeof(x)-1) == 0)
+
+static void
+mag_readmsg(gpsdata_type objective)
+{
+  char ibuf[512];      /* oliskoli: corrupted data (I've seen descr with a lot
+                                    of escaped FFFFFFFF) may need more size  */
+  int retrycnt = 20;
+
+retry:
+  QString gr = termread(ibuf, sizeof(ibuf));
+
+  if (gr.isEmpty()) {
+    if (!got_version) {
+      /*
+       * The 315 can take up to six seconds to respond to
+       * a VERSION command.   Since this is on startup,
+       * we'll be fairly persistent in retrying.
+       */
+      if (retrycnt--) {
+        goto retry;
+      } else {
+        fatal(MYNAME ": No data received from GPS.\n");
+      }
+    } else {
+      if (is_file)  {
+        found_done = 1;
+      }
+      return;
+    }
+  }
+
+  /* If column zero isn't a dollar sign, it's not for us */
+  if (ibuf[0] != '$') {
+    fatal(MYNAME ": line doesn't start with '$'.\n");
+  }
+
+
+  int isz = strlen(ibuf);
+
+  if (isz < 5) {
+    if (debug_serial) {
+      warning("SHORT READ %d\n", isz);
+    }
+    return;
+  }
+  mag_error = 0;
+  while (!isprint(ibuf[isz])) {
+    isz--;
+  }
+  char* isump = &ibuf[isz-1];
+  unsigned int isum = strtoul(isump, nullptr,16);
+  if (isum != mag_pchecksum(&ibuf[1], isz-3)) {
+    if (debug_serial) {
+      warning("RXERR %02x/%02x: '%s'\n", isum, mag_pchecksum(&ibuf[1],isz-5), ibuf);
+    }
+    /* Special case receive errors early on. */
+    if (!got_version) {
+      fatal(MYNAME ": bad communication.  Check bit rate.\n");
+    }
+  }
+  if (debug_serial) {
+    warning("READ: %s\n", ibuf);
+  }
+  if (IS_TKN("$PMGNCSM,")) {
+    last_rx_csum = strtoul(&ibuf[9], nullptr, 16);
+    magrxstate = mrs_handon;
+    return;
+  }
+  if (strncmp(ibuf, "$PMGNWPL,", 7) == 0) {
+    Waypoint* wpt = mag_wptparse(ibuf);
+    waypoint_read_count++;
+    if (global_opts.verbose_status) {
+      waypt_status_disp(waypoint_read_count,
+                        waypoint_read_count);
+    }
+
+    if (extension_hint) {
+      if (extension_hint == WPTDATAMASK) {
+        waypt_add(wpt);
+      } else if (extension_hint == RTEDATAMASK) {
+        rte_wpt_tmp.append(wpt);
+      }
+    } else {
+      switch (objective) {
+      case wptdata:
+        waypt_add(wpt);
+        break;
+      case rtedata:
+        rte_wpt_tmp.append(wpt);
+        break;
+      default:
+        break;
+      }
+    }
+  }
+  if (strncmp(ibuf, "$PMGNTRK,", 7) == 0) {
+    Waypoint* wpt = mag_trkparse(ibuf);
+    /*
+     * Allow lazy allocation of track head.
+     */
+    if (trk_head == nullptr) {
+      /* These tracks don't have names, so derive one
+       * from input filename.
+       */
+
+      trk_head = new route_head;
+
+      /* Whack trailing extension if present. */
+      QString s = get_filename(curfname);
+      int idx = s.indexOf('.');
+      if (idx > 0) {
+        s.truncate(idx);
+      }
+
+      trk_head->rte_name = s;
+      track_add_head(trk_head);
+    }
+
+    track_add_wpt(trk_head, wpt);
+  }
+  if (strncmp(ibuf, "$PMGNRTE,", 7) == 0) {
+    mag_rteparse(ibuf);
+  }
+  if (IS_TKN("$PMGNVER,")) {
+    mag_verparse(ibuf);
+  }
+  mag_error = 0;
+  if (!ignore_unable && IS_TKN("$PMGNCMD,UNABLE")) {
+    warning("Unable to send\n");
+    found_done = 1;
+    mag_error = 1;
+    ignore_unable = 0;
+    return;
+  }
+  if (IS_TKN("$PMGNCMD,END") || (is_file && (gbfeof(magfile_h)))) {
+    found_done = 1;
+    return;
+  }
+
+  if (magrxstate != mrs_handoff) {
+    mag_writeack(isum);
+  }
+}
+
+static void* serial_handle = nullptr;
+
+static int
+terminit(const QString& portname, int create_ok)
+{
+  if (gbser_is_serial(qPrintable(portname))) {
+    if (serial_handle = gbser_init(qPrintable(portname)), nullptr != serial_handle) {
+      int rc;
+      if (rc = gbser_set_port(serial_handle, bitrate, 8, 0, 1), gbser_OK != rc) {
+        fatal(MYNAME ": Can't configure port\n");
+      }
+    }
+    is_file = 0;
+    if (serial_handle == nullptr) {
+      fatal(MYNAME ": Could not open serial port %s\n", qPrintable(portname));
+    }
+    return 1;
+  } else {
+    /* Does this check for an error? */
+    magfile_h = gbfopen(portname, create_ok ? "w+b" : "rb", MYNAME);
+    is_file = 1;
+    icon_mapping = map330_icon_table;
+    mag_cleanse = m330_cleanse;
+    got_version = 1;
+    return 0;
+  }
+}
+
+static QString termread(char* ibuf, int size)
+{
+  if (is_file) {
+    return gbfgets(ibuf, size, magfile_h);
+  } else {
+    int rc = gbser_read_line(serial_handle, ibuf, size, 2000, 0x0a, 0x0d);
+    if (rc != gbser_OK) {
+      fatal(MYNAME ": Read error\n");
+    }
+    return ibuf;
+  }
+}
+
+/* Though not documented in the protocol spec, if the unit itself
+ * wants to create a field containing a comma, it will encode it
+ * as <escape>2C.  We extrapolate that any 2 digit hex encoding may
+ * be valid.  We don't do this in termread() since we need to do it
+ * after the scanf.  This means we have to do it field-by-field
+ * basis.
+ *
+ * The buffer is modified in place and shortened by copying the remaining
+ * string including the terminator.
+ */
+static
+void
+mag_dequote(char* ibuf)
+{
+  char* esc = nullptr;
+
+  while ((esc = strchr(ibuf, 0x1b))) {
+    int nremains = strlen(esc);
+    if (nremains >= 3) {
+      static const char hex[17] = "0123456789ABCDEF";
+      const char* c1 = strchr(hex, esc[1]);
+      const char* c2 = strchr(hex, esc[2]);
+      if (c1 && c2) {
+        int escv = (c1 - hex) * 16 + (c2 - hex);
+        if (escv == 255) {     /* corrupted data */
+          char* tmp = esc + 1;
+          while (*tmp == 'F') {
+            tmp++;
+          }
+          memmove(esc, tmp, strlen(tmp) + 1);
+        } else {
+          *esc++ = (isprint(escv)) ? escv : '$';
+          /* buffers overlap */
+          memmove(esc, esc+2, nremains - 2);
+        }
+      }
+    } else {
+      *esc = '\0';     /* trim corrupted data,
+                                          otherwise we get an endless loop */
+    }
+  }
+}
+
+static void
+termwrite(const char* obuf, int size)
+{
+  if (is_file) {
+    size_t nw;
+    if (nw = gbfwrite(obuf, 1, size, magfile_h), nw < (size_t) size) {
+      fatal(MYNAME ": Write error");
+    }
+  } else {
+    int rc;
+    if (rc = gbser_write(serial_handle, obuf, size), rc < 0) {
+      fatal(MYNAME ": Write error");
+    }
+  }
+}
+
+static void termdeinit()
+{
+  if (is_file) {
+    gbfclose(magfile_h);
+    magfile_h = nullptr;
+  } else {
+    gbser_deinit(serial_handle);
+    serial_handle = nullptr;
+  }
+}
+
+/*
+ *  Arg tables are doubled up so that -? can output appropriate help
+ */
+static
+QVector<arglist_t> mag_sargs = {
+  {
+    "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING,
+    ARG_NOMINMAX, nullptr
+  },
+  {
+    "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)",
+    "200", ARGTYPE_INT, ARG_NOMINMAX, nullptr
+  },
+  {
+    "baud", &bs, "Numeric value of bitrate (baud=4800)", "4800",
+    ARGTYPE_INT, ARG_NOMINMAX, nullptr
+  },
+  {
+    "noack", &noack, "Suppress use of handshaking in name of speed",
+    nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
+  },
+  {
+    "nukewpt", &nukewpt, "Delete all waypoints", nullptr, ARGTYPE_BOOL,
+    ARG_NOMINMAX, nullptr
+  },
+};
+
+static
+QVector<arglist_t> mag_fargs = {
+  {
+    "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING,
+    ARG_NOMINMAX, nullptr
+  },
+  {
+    "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)",
+    nullptr, ARGTYPE_INT, ARG_NOMINMAX, nullptr
+  },
+};
+
+/*
+ * The part of the serial init that's common to read and write.
+ */
+static void
+mag_serial_init_common(const QString& portname)
+{
+  if (is_file) {
+    return;
+  }
+
+  mag_handoff();
+  if (!noack && !suppress_ack) {
+    mag_handon();
+  }
+
+  time_t now = current_time().toTime_t();
+  /*
+   * The 315 can take up to 4.25 seconds to respond to initialization
+   * commands.   Time out on the side of caution.
+   */
+  time_t later = now + 6;
+  got_version = 0;
+  mag_writemsg("PMGNCMD,VERSION");
+
+  while (!got_version) {
+    mag_readmsg(trkdata);
+    if (current_time().toTime_t() > later) {
+      fatal(MYNAME ": No acknowledgment from GPS on %s\n",
+            qPrintable(portname));
+    }
+  }
+
+  if ((icon_mapping != gps315_icon_table)) {
+    /*
+     * The 315 can't handle this command, so we set a global
+     * to ignore the NAK on it.
+     */
+    ignore_unable = 1;
+    mag_writemsg("PMGNCMD,NMEAOFF");
+    ignore_unable = 0;
+  }
+
+  if (nukewpt) {
+    /* The unit will send us an "end" message upon completion */
+    mag_writemsg("PMGNCMD,DELETE,WAYPOINT");
+    mag_readmsg(trkdata);
+    if (!found_done) {
+      fatal(MYNAME ": Unexpected response to waypoint delete command.\n");
+    }
+    found_done = 0;
+  }
+
+}
+static void
+mag_rd_init_common(const QString& portname)
+{
+  waypoint_read_count = 0;
+  // For Explorist GC, intercept the device access and redirect to GPX.
+  // We actually do the rd_init() inside read as we may have multiple
+  // files that we have to read.
+  if (portname == "usb:") {
+    const char** dlist = os_get_magellan_mountpoints();
+    explorist_info = explorist_ini_get(dlist);
+    if (explorist_info) {
+      gpx_vec = Vecs::Instance().find_vec("gpx");
+    }
+    return;
+  }
+
+  if (bs) {
+    bitrate=xstrtoi(bs, nullptr, 10);
+  }
+
+  if (!mkshort_handle) {
+    mkshort_handle = mkshort_new_handle();
+  }
+
+  terminit(portname, 0);
+  mag_serial_init_common(portname);
+
+  rte_wpt_tmp.clear();
+
+  /* find the location of the tail of the path name,
+   * make a copy of it, then lop off the file extension
+   */
+
+  curfname = get_filename(portname);
+
+  /*
+   * I'd rather not derive behaviour from filenames but since
+   * we can't otherwise tell if we should put a WPT on the route
+   * queue or the WPT queue in the presence of (-w -r -t) we
+   * divine a hint from the filename extension when we can.
+   */
+  QString exten = QFileInfo(curfname).suffix();
+  if (exten.length() > 0) {
+    if (0 == exten.compare(QLatin1String("upt"), Qt::CaseInsensitive)) {
+      extension_hint = WPTDATAMASK;
+    } else if (0 == exten.compare(QLatin1String("log"), Qt::CaseInsensitive)) {
+      extension_hint = TRKDATAMASK;
+    } else if (0 == exten.compare(QLatin1String("rte"), Qt::CaseInsensitive)) {
+      extension_hint = RTEDATAMASK;
+    }
+  }
+
+}
+
+static void
+mag_rd_init(const QString& portname)
+{
+  explorist = 0;
+  suppress_ack = 1;
+  mag_rd_init_common(portname);
+}
+
+static void
+magX_rd_init(const QString& portname)
+{
+  explorist = 1;
+  mag_rd_init_common(portname);
+}
+
+static void
+mag_wr_init_common(const QString& portname)
+{
+  suppress_ack = 0;
+  if (bs) {
+    bitrate=xstrtoi(bs, nullptr, 10);
+  }
+
+  if (waypt_count() > 500) {
+    fatal(MYNAME ": Meridian/Explorist does not support more than 500 waypoints in one file. Only\n200 waypoints may have comments.\nDecrease the number of waypoints sent.\n");
+  }
+
+  if (cmts) {
+    wptcmtcnt_max = xstrtoi(cmts, nullptr, 10);
+  } else {
+    wptcmtcnt_max = MAXCMTCT ;
+  }
+
+  if (!mkshort_handle) {
+    mkshort_handle = mkshort_new_handle();
+  }
+
+  terminit(portname, 1);
+  mag_serial_init_common(portname);
+
+  rte_wpt_tmp.clear();
+}
+
+/*
+ * Entry point for extended (explorist) points.
+ */
+static void
+magX_wr_init(const QString& portname)
+{
+  wpt_len = 20;
+  explorist = 1;
+  mag_wr_init_common(portname);
+  setshort_length(mkshort_handle, wpt_len);
+  setshort_whitespace_ok(mkshort_handle, 1);
+}
+
+static void
+mag_wr_init(const QString& portname)
+{
+  explorist = 0;
+  wpt_len = 8;
+  mag_wr_init_common(portname);
+  /*
+   * Whitespace is actually legal, but since waypoint name length is
+   * only 8 bytes, we'll conserve them.
+   */
+
+  setshort_whitespace_ok(mkshort_handle, 0);
+}
+
+static void
+mag_deinit()
+{
+  if (explorist_info) {
+    explorist_ini_done(explorist_info);
+    return;
+  }
+  mag_handoff();
+  termdeinit();
+  if (mkshort_handle) {
+    mkshort_del_handle(&mkshort_handle);
+  }
+
+  while (!rte_wpt_tmp.isEmpty()) {
+    delete rte_wpt_tmp.takeFirst();
+  }
+
+  trk_head = nullptr;
+
+  curfname.clear();
+}
+
+static void
+mag_wr_deinit()
+{
+  if (explorist) {
+    mag_writemsg("PMGNCMD,END");
+  }
+  mag_deinit();
+}
+
+/*
+ * I'm tired of arguing with scanf about optional fields .  Detokenize
+ * an incoming string that may contain empty fields.
+ *
+ * Probably should be cleaned up and moved to common code, but
+ * making it deal with an arbitrary number of fields of arbitrary
+ * size is icky.  We don't have to solve the general case here...
+ */
+
+static char ifield[20][100];
+static
+void parse_istring(char* istring)
+{
+  int f = 0;
+  int n;
+  while (istring[0]) {
+    char* fp = ifield[f];
+    int x = sscanf(istring, "%[^,]%n", fp, &n);
+    f++;
+    if (x) {
+      istring += n;
+      /* IF more in this string, skip delim */
+      if (istring[0]) {
+        istring++;
+      }
+    } else {
+      istring ++;
+    }
+  }
+}
+
+/*
+ * Given an incoming track messages of the form:
+ * $PMGNTRK,3605.259,N,08644.389,W,00151,M,201444.61,A,,020302*66
+ * create and return a populated waypoint.
+ */
+Waypoint*
+mag_trkparse(char* trkmsg)
+{
+  int hms;
+  int fracsecs;
+  struct tm tm;
+
+  auto* waypt = new Waypoint;
+
+  memset(&tm, 0, sizeof(tm));
+
+  /*
+   * As some of the fields are optional, sscanf works badly
+   * for us.
+   */
+  parse_istring(trkmsg);
+  double latdeg = strtod(ifield[1], nullptr);
+  char latdir = ifield[2][0];
+  double lngdeg = strtod(ifield[3], nullptr);
+  char lngdir = ifield[4][0];
+  int alt = strtod(ifield[5], nullptr);
+  char altunits = ifield[6][0];
+  (void)altunits;
+  sscanf(ifield[7], "%d.%d", &hms, &fracsecs);
+  /* Field 8 is constant */
+  /* Field nine is optional track name */
+  int dmy = xstrtoi(ifield[10], nullptr, 10);
+  int sec = hms % 100;
+  hms = hms / 100;
+  int min = hms % 100;
+  hms = hms / 100;
+  int hour = hms % 100;
+
+  int year = 100 + dmy % 100 + 1900;
+  dmy = dmy / 100;
+  int mon =  dmy % 100;
+  dmy = dmy / 100;
+  int day = dmy % 100;
+  QDateTime dt(QDate(year, mon, day), QTime(hour, min, sec, fracsecs * 10), Qt::UTC);
+  waypt->SetCreationTime(dt);
+
+  if (latdir == 'S') {
+    latdeg = -latdeg;
+  }
+  waypt->latitude = ddmm2degrees(latdeg);
+
+  if (lngdir == 'W') {
+    lngdeg = -lngdeg;
+  }
+  waypt->longitude = ddmm2degrees(lngdeg);
+
+  waypt->altitude = alt;
+
+  return waypt;
+
+}
+
+/*
+ * Given an incoming route messages of the form:
+ * $PMGNRTE,4,1,c,1,DAD,a,Anna,a*61
+ * generate a route.
+ */
+void
+mag_rteparse(char* rtemsg)
+{
+  int n;
+  int frags,frag,rtenum;
+  char xbuf[100],next_stop[100],abuf[100];
+  char* currtemsg;
+  static mag_rte_head_t* mag_rte_head;
+
+#if 0
+  sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n",
+         &frags,&frag,xbuf,&rtenum,&n);
+#else
+  sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n",
+         &frags,&frag,xbuf,&rtenum,&n);
+
+  /* Explorist has a route name here */
+  QString rte_name;
+  if (explorist) {
+    char* ca = rtemsg + n;
+    if (*ca++ != ',') {
+      fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg);
+    }
+
+    char* ce = strchr(ca, ',');
+    if (ce == nullptr) {
+      fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg);
+    }
+
+    if (ca == ce) {
+      rte_name = "Route";
+      rte_name += QString::number(rtenum);
+    } else {
+      rte_name = ca;
+      rte_name.truncate(ce-ca);
+    }
+
+    n += ((ce - ca) + 1);
+  }
+
+#endif
+
+  /*
+   * This is the first component of a route.  Allocate a new
+   * head.
+   */
+  if (frag == 1) {
+    mag_rte_head = new mag_rte_head_t;
+    mag_rte_head->nelems = frags;
+  }
+
+  currtemsg = rtemsg + n;
+
+  /*
+   * The individual line may contain several route elements.
+   * loop and pick those up.
+   */
+  while (sscanf(currtemsg,",%[^,],%[^,]%n",next_stop, abuf,&n)) {
+    if ((next_stop[0] == 0) || (next_stop[0] == '*')) {
+      break;
+    }
+
+    /* trim CRC from waypoint icon string */
+    if (char* p = strchr(abuf, '*'); p != nullptr) {
+      *p = '\0';
+    }
+
+    auto* rte_elem = new mag_rte_elem;
+
+    rte_elem->wpt_name = next_stop;
+    rte_elem->wpt_icon = abuf;
+
+    mag_rte_head->elem_list.append(rte_elem);
+
+    /* Sportrak (the non-mapping unit) creates malformed
+     * RTE sentence with no icon info after the routepoint
+     * name.  So if we saw an "icon" treat that as new
+     * routepoint.
+     */
+    if (broken_sportrak && abuf[0]) {
+      rte_elem = new mag_rte_elem;
+      rte_elem->wpt_name = abuf;
+
+      mag_rte_head->elem_list.append(rte_elem);
+    }
+
+    next_stop[0] = 0;
+    currtemsg += n;
+  }
+
+  /*
+   * If this was the last fragment of the route, add it to the
+   * gpsbabel internal structs now.
+   */
+  if (frag == mag_rte_head->nelems) {
+
+    auto* rte_head = new route_head;
+    route_add_head(rte_head);
+    rte_head->rte_num = rtenum;
+    rte_head->rte_name = rte_name;
+
+    /*
+     * It is quite feasible that we have 200 waypoints,
+     * 3 of which are used in the route.  We'll need to find
+     * those in the queue for SD routes...
+     */
+
+    while (!mag_rte_head->elem_list.isEmpty()) {
+      mag_rte_elem* re = mag_rte_head->elem_list.takeFirst();
+
+      /*
+       * Copy route points from temp wpt queue.
+       */
+      foreach (const Waypoint* waypt, rte_wpt_tmp) {
+        if (waypt->shortname == re->wpt_name) {
+          auto* wpt = new Waypoint(*waypt);
+          route_add_wpt(rte_head, wpt);
+          break;
+        }
+      }
+
+      delete re;
+    }
+    delete mag_rte_head;
+  }
+}
+
+QString
+mag_find_descr_from_token(const char* token)
+{
+  if (icon_mapping == nullptr) {
+    return "unknown";
+  }
+
+  for (const magellan_icon_mapping_t* i = icon_mapping; i->token; i++) {
+    if (token[0] == 0) {
+      break;
+    }
+    if (case_ignore_strcmp(token, i->token) == 0) {
+      return i->icon;
+    }
+  }
+  return icon_mapping[0].icon;
+}
+
+QString
+mag_find_token_from_descr(const QString& icon)
+{
+  const magellan_icon_mapping_t* i = icon_mapping;
+
+  if (i == nullptr || icon == nullptr) {
+    return "a";
+  }
+
+  for (i = icon_mapping; i->token; i++) {
+    if (icon.compare(i->icon, Qt::CaseInsensitive) == 0) {
+      return i->token;
+    }
+  }
+  return icon_mapping[0].token;
+}
+
+/*
+ * Given an incoming waypoint messages of the form:
+ * $PMGNWPL,3549.499,N,08650.827,W,0000257,M,HOME,HOME,c*4D
+ * create and return a populated waypoint.
+ */
+static Waypoint*
+mag_wptparse(char* trkmsg)
+{
+  double latdeg, lngdeg;
+  char latdir;
+  char lngdir;
+  int alt;
+  char altunits;
+  char shortname[100];
+  char descr[256];
+  char icon_token[100];
+  int i = 0;
+
+  descr[0] = 0;
+  icon_token[0] = 0;
+
+  auto* waypt = new Waypoint;
+
+  sscanf(trkmsg,"$PMGNWPL,%lf,%c,%lf,%c,%d,%c,%[^,],%[^,]",
+         &latdeg,&latdir,
+         &lngdeg,&lngdir,
+         &alt,&altunits,shortname,descr);
+  char* icone = strrchr(trkmsg, '*');
+  char* icons = strrchr(trkmsg, ',')+1;
+
+  mag_dequote(descr);
+
+  for (char* blah = icons ; blah < icone; blah++) {
+    icon_token[i++] = *blah;
+  }
+  icon_token[i++] = '\0';
+
+  if (latdir == 'S') {
+    latdeg = -latdeg;
+  }
+  waypt->latitude = ddmm2degrees(latdeg);
+
+  if (lngdir == 'W') {
+    lngdeg = -lngdeg;
+  }
+  waypt->longitude = ddmm2degrees(lngdeg);
+
+  waypt->altitude = alt;
+  waypt->shortname = shortname;
+  waypt->description = descr;
+  waypt->icon_descr = mag_find_descr_from_token(icon_token);
+
+  return waypt;
+}
+
+static void
+mag_read()
+{
+  if (gpx_vec) {
+    QStringList f = os_gpx_files(explorist_info->track_path);
+    for (const auto& file : qAsConst(f)) {
+      gpx_vec->rd_init(file);
+      gpx_vec->read();
+      gpx_vec->rd_deinit();
+    }
+
+    f = os_gpx_files(explorist_info->waypoint_path);
+    for (const auto& file : qAsConst(f)) {
+      gpx_vec->rd_init(file);
+      gpx_vec->read();
+      gpx_vec->rd_deinit();
+    }
+#if 0
+    f = os_gpx_files(explorist_info->geo_path);
+    for (const auto& file : qAsConst(f)) {
+      gpx_vec->rd_init(file);
+      gpx_vec->read();
+      gpx_vec->rd_deinit();
+    }
+#endif
+    return;
+  }
+
+  found_done = 0;
+  if (global_opts.masked_objective & TRKDATAMASK) {
+    magrxstate = mrs_handoff;
+    if (!is_file) {
+      mag_writemsg("PMGNCMD,TRACK,2");
+    }
+
+    while (!found_done) {
+      mag_readmsg(trkdata);
+    }
+  }
+
+  found_done = 0;
+  if (global_opts.masked_objective & WPTDATAMASK) {
+    magrxstate = mrs_handoff;
+    if (!is_file) {
+      mag_writemsg("PMGNCMD,WAYPOINT");
+    }
+
+    while (!found_done) {
+      mag_readmsg(wptdata);
+    }
+  }
+
+  found_done = 0;
+  if (global_opts.masked_objective & RTEDATAMASK) {
+    magrxstate = mrs_handoff;
+    if (!is_file) {
+      /*
+       * serial routes require waypoint & routes
+       * messages commands.
+       */
+      mag_writemsg("PMGNCMD,WAYPOINT");
+
+      while (!found_done) {
+        mag_readmsg(rtedata);
+      }
+
+      mag_writemsg("PMGNCMD,ROUTE");
+
+      found_done = 0;
+      while (!found_done) {
+        mag_readmsg(rtedata);
+      }
+    } else {
+      /*
+       * SD routes are a stream of PMGNWPL and
+       * PMGNRTE messages, in that order.
+       */
+      while (!found_done) {
+        mag_readmsg(rtedata);
+      }
+    }
+  }
+}
+
+static
+void
+mag_waypt_pr(const Waypoint* waypointp)
+{
+  QScopedPointer<char, QScopedPointerPodDeleter> obuf;
+  QScopedPointer<char, QScopedPointerPodDeleter> ofmtdesc;
+  QString icon_token;
+
+  double ilat = waypointp->latitude;
+  double ilon = waypointp->longitude;
+
+  double lon = fabs(ilon);
+  double lat = fabs(ilat);
+
+  int lon_deg = lon;
+  int lat_deg = lat;
+
+  lon = (lon - lon_deg) * 60.0;
+  lat = (lat - lat_deg) * 60.0;
+
+  lon = (lon_deg * 100.0 + lon);
+  lat = (lat_deg * 100.0 + lat);
+
+  if (deficon)  {
+    icon_token = mag_find_token_from_descr(deficon);
+  } else {
+    icon_token = mag_find_token_from_descr(waypointp->icon_descr);
+  }
+
+  if (!get_cache_icon(waypointp).isEmpty()) {
+    icon_token = mag_find_token_from_descr(get_cache_icon(waypointp));
+  }
+
+  QString isrc = waypointp->notes.isEmpty() ? waypointp->description : waypointp->notes;
+  QString owpt = global_opts.synthesize_shortnames ?
+         mkshort_from_wpt(mkshort_handle, waypointp) : waypointp->shortname;
+  QString odesc = isrc;
+  owpt = mag_cleanse(CSTRc(owpt));
+
+  if (global_opts.smart_icons &&
+      waypointp->gc_data->diff && waypointp->gc_data->terr) {
+    // It's a string and compactness counts, so "1.0" is OK to be "10".
+    xasprintf(ofmtdesc, "%ud/%ud %s", waypointp->gc_data->diff,
+            waypointp->gc_data->terr, CSTRc(odesc));
+    odesc = mag_cleanse(ofmtdesc.data());
+  } else {
+    odesc = mag_cleanse(CSTRc(odesc));
+  }
+
+  /*
+   * For the benefit of DirectRoute (which uses waypoint comments
+   * to deliver turn-by-turn popups for street routing) allow a
+   * cap on the comments delivered so we leave space for it to route.
+   */
+  if (!odesc.isEmpty() && (wptcmtcnt++ >= wptcmtcnt_max)) {
+    odesc.clear();
+  }
+
+  xasprintf(obuf, "PMGNWPL,%4.3f,%c,%09.3f,%c,%07.0f,M,%-.*s,%-.46s,%s",
+          lat, ilat < 0 ? 'S' : 'N',
+          lon, ilon < 0 ? 'W' : 'E',
+          waypointp->altitude == unknown_alt ?
+          0 : waypointp->altitude,
+          wpt_len,
+          CSTRc(owpt),
+          CSTRc(odesc),
+          CSTR(icon_token));
+  mag_writemsg(obuf.data());
+
+  if (!is_file) {
+    if (mag_error) {
+      warning("Protocol error Writing '%s'\n", obuf.data());
+    }
+  }
+}
+
+static
+void mag_track_disp(const Waypoint* waypointp)
+{
+  QScopedPointer<char, QScopedPointerPodDeleter> obuf;
+
+  double ilat = waypointp->latitude;
+  double ilon = waypointp->longitude;
+
+  QByteArray dmy("");
+  QByteArray hms("");
+  if (waypointp->creation_time.isValid()) {
+    // Round to hundredths of seconds before conversion to string.
+    // Rounding can ripple all the way from the msec to the year.
+    QDateTime dt = waypointp->GetCreationTime().toUTC();
+    dt = dt.addMSecs(10 * lround(dt.time().msec()/10.0) - dt.time().msec());
+    assert((dt.time().msec() % 10) == 0);
+    dmy = dt.toString(u"ddMMyy").toUtf8();
+    hms = dt.toString(u"hhmmss.zzz").left(9).toUtf8();
+  }
+
+  double lon = fabs(ilon);
+  double lat = fabs(ilat);
+
+  int lon_deg = lon;
+  int lat_deg = lat;
+
+  lon = (lon - lon_deg) * 60.0;
+  lat = (lat - lat_deg) * 60.0;
+
+  lon = (lon_deg * 100.0 + lon);
+  lat = (lat_deg * 100.0 + lat);
+
+  xasprintf(obuf,"PMGNTRK,%4.3f,%c,%09.3f,%c,%05.0f,%c,%s,A,,%s",
+          lat, ilat < 0 ? 'S' : 'N',
+          lon, ilon < 0 ? 'W' : 'E',
+          waypointp->altitude == unknown_alt ?
+          0 : waypointp->altitude,
+          'M', hms.constData(), dmy.constData());
+  mag_writemsg(obuf.data());
+}
+
+static
+void mag_track_pr()
+{
+  track_disp_all(nullptr, nullptr, mag_track_disp);
+}
+
+/*
+The spec says to stack points:
+       $PMGNRTE,2,1,c,1,FOO,POINT1,b,POINT2,c,POINT3,d*6C<CR><LF>
+
+Meridian SD card and serial (at least) writes in pairs:
+       $PMGNRTE,4,1,c,1,HOME,c,I49X73,a*15
+       ...
+       $PMGNRTE,4,4,c,1,RON273,a,MYCF93,a*7B
+
+The spec also says that some units don't like single-legged pairs,
+and to replace the 2nd name with "<<>>", but I haven't seen one of those.
+*/
+
+static void
+mag_route_trl(const route_head* rte)
+{
+  QScopedPointer<char, QScopedPointerPodDeleter> obuff;
+  QString buff1;
+  QString buff2;
+  QString icon_token;
+
+  /* count waypoints for this route */
+  int i = rte->rte_waypt_ct();
+
+  /* number of output PMGNRTE messages at 2 points per line */
+  int numlines = (i / 2) + (i % 2);
+
+  /* increment the route counter. */
+  route_out_count++;
+
+  int thisline = i = 0;
+  foreach (const Waypoint* waypointp, rte->waypoint_list) {
+    i++;
+
+    if (deficon) {
+      icon_token = mag_find_token_from_descr(deficon);
+    } else {
+      icon_token = mag_find_token_from_descr(waypointp->icon_descr);
+    }
+
+    QString* pbuff;
+    if (i == 1) {
+      pbuff = &buff1;
+    } else {
+      pbuff = &buff2;
+    }
+    // Write name, icon tuple into alternating buff1/buff2 buffer.
+    *pbuff = waypointp->shortname + ',' + icon_token;
+
+    if ((waypointp == rte->waypoint_list.back()) || ((i % 2) == 0)) {
+      QString expbuf;
+      thisline++;
+      if (explorist) {
+        expbuf = rte->rte_name + ',';
+      }
+
+      xasprintf(obuff, "PMGNRTE,%d,%d,c,%d,%s%s,%s",
+              numlines, thisline,
+              rte->rte_num ? rte->rte_num : route_out_count,
+              CSTRc(expbuf),
+              CSTR(buff1), CSTR(buff2));
+
+      mag_writemsg(obuff.data());
+      buff1.clear();
+      buff2.clear();
+      i = 0;
+    }
+  }
+}
+
+static void
+mag_route_pr()
+{
+  route_out_count = 0;
+  route_disp_all(nullptr, mag_route_trl, mag_waypt_pr);
+
+}
+
+static void
+mag_write()
+{
+
+  wptcmtcnt = 0;
+
+  switch (global_opts.objective) {
+  case trkdata:
+    mag_track_pr();
+    break;
+  case wptdata:
+    waypt_disp_all(mag_waypt_pr);
+    break;
+  case rtedata:
+    mag_route_pr();
+    break;
+  default:
+    fatal(MYNAME ": Unknown objective.\n");
+  }
+}
+
+const char** os_get_magellan_mountpoints()
+{
+#if __APPLE__
+  const char** dlist = (const char**) xcalloc(2, sizeof *dlist);
+  dlist[0] = xstrdup("/Volumes/Magellan");
+  dlist[1] = nullptr;
+  return dlist;
+#else
+  fatal("Not implemented");
+  return nullptr;
+#endif
+}
+
+static QStringList
+os_gpx_files(const char* dirname)
+{
+  QDir dir(dirname);
+
+  const QFileInfoList filist = dir.entryInfoList(QStringList("*.gpx"), QDir::Files | QDir::Readable, QDir::Name);
+  QStringList rv;
+  for (const auto& fi : filist) {
+    rv.append(fi.absoluteFilePath());
+  }
+  return rv;
+}
+
+/*
+ *  This is repeated just so it shows up as separate menu options
+ *  for the benefit of GUI wrappers.
+ */
+ff_vecs_t mag_svecs = {
+  ff_type_serial,
+  FF_CAP_RW_ALL,
+  mag_rd_init,
+  mag_wr_init,
+  mag_deinit,
+  mag_deinit,
+  mag_read,
+  mag_write,
+  nullptr,
+  &mag_sargs,
+  CET_CHARSET_ASCII, 0,        /* CET-REVIEW */
+  NULL_POS_OPS
+};
+
+ff_vecs_t mag_fvecs = {
+  ff_type_file,
+  FF_CAP_RW_ALL,
+  mag_rd_init,
+  mag_wr_init,
+  mag_deinit,
+  mag_deinit,
+  mag_read,
+  mag_write,
+  nullptr,
+  &mag_fargs,
+  CET_CHARSET_ASCII, 0,        /* CET-REVIEW */
+  NULL_POS_OPS
+};
+
+/*
+ * Extended (Explorist) entry tables.
+ */
+ff_vecs_t magX_fvecs = {
+  ff_type_file,
+  FF_CAP_RW_ALL,
+  magX_rd_init,
+  magX_wr_init,
+  mag_deinit,
+  mag_wr_deinit,
+  mag_read,
+  mag_write,
+  nullptr,
+  &mag_fargs,
+  CET_CHARSET_ASCII, 0,        /* CET-REVIEW */
+  NULL_POS_OPS
+};
diff --git a/explorist_ini.cc b/explorist_ini.cc
deleted file mode 100644 (file)
index e9eafdb..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "defs.h"
-#include "explorist_ini.h"
-#include "inifile.h"
-
-static inifile_t* inifile;
-static const char myname[] = "explorist";
-
-#ifdef DEAD_CODE_IS_REBORN
-static const char*
-explorist_read_value(const char* section, const char* key)
-{
-  return inifile_readstr(inifile, section, key);
-}
-#endif
-
-static mag_info*
-explorist_ini_try(const char* path)
-{
-  char* inipath;
-
-  xasprintf(&inipath, "%s/%s", path, "APP/Atlas.ini");
-  inifile = inifile_init(QString::fromUtf8(inipath), myname);
-  if (!inifile) {
-    xfree(inipath);
-    return nullptr;
-  }
-
-  auto* info = (mag_info*) xmalloc(sizeof(mag_info));
-  info->geo_path = nullptr;
-  info->track_path = nullptr;
-  info->waypoint_path = nullptr;
-
-  QString s = inifile_readstr(inifile,  "UGDS", "WpFolder");
-  if (!s.isNull()) {
-    s.replace('\\', '/');
-    xasprintf(&info->waypoint_path, "%s/%s", path, CSTR(s));
-  }
-  s = inifile_readstr(inifile,  "UGDS", "GcFolder");
-  if (!s.isNull()) {
-    s.replace('\\', '/');
-    xasprintf(&info->geo_path, "%s/%s", path, CSTR(s));
-  }
-  s = inifile_readstr(inifile,  "UGDS", "TrkFolder");
-  if (!s.isNull()) {
-    s.replace('\\', '/');
-    xasprintf(&info->track_path, "%s/%s", path, CSTR(s));
-  }
-
-  inifile_done(inifile);
-  xfree(inipath);
-  return info;
-}
-
-mag_info*
-explorist_ini_get(const char** dirlist)
-{
-  mag_info* r = nullptr;
-  while (dirlist && *dirlist) {
-    r = explorist_ini_try(*dirlist);
-    if (r) {
-      return r;
-    }
-  }
-  return r;
-}
-
-void
-explorist_ini_done(mag_info* info)
-{
-  xfree(info->geo_path);
-  xfree(info->track_path);
-  xfree(info->waypoint_path);
-  xfree(info);
-}
diff --git a/explorist_ini.h b/explorist_ini.h
deleted file mode 100644 (file)
index 078c0dc..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-
-/*
- * Interesting traits of the device from the *.ini files.
- */
-struct mag_info {
-  char* geo_path;
-  char* track_path;
-  char* waypoint_path;
-};
-
-mag_info* explorist_ini_get(const char** directory_list);
-void explorist_ini_done(mag_info* info);
diff --git a/magellan.h b/magellan.h
deleted file mode 100644 (file)
index 67f0626..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-    Copyright (C) 2002-2005 Robert Lipe, robertlipe@usa.net
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-
- */
-
-#ifndef MAGELLAN_H_INCLUDED_
-#define MAGELLAN_H_INCLUDED_
-
-#include <QString>
-#include "defs.h"
-
-/*
- * Table of "interesting" Magellan models.
- * Selfishly, if I haven't heard of it, it's not in the table.
- * This doesn't mean I actually have TRIED all models listed below.
- * (Donations welcome. :-)
- */
-enum meridian_model {
-  mm_unknown = 0 ,
-  mm_gps315320,
-  mm_map410,
-  mm_map330,
-  mm_gps310,
-  mm_meridian,
-  mm_sportrak
-};
-
-struct pid_to_model_t {
-  meridian_model model;
-  int pid;
-  const char* model_n;
-};
-
-struct magellan_icon_mapping_t {
-  const char* token;
-  const char* icon;
-};
-
-QString mag_find_descr_from_token(const char* token);
-QString mag_find_token_from_descr(const QString& icon);
-
-unsigned int mag_checksum(const char*buf);
-QString m330_cleanse(const char* istring);
-
-Waypoint* mag_trkparse(char* trkmsg);
-void mag_rteparse(char* rtemsg);
-
-#endif  // MAGELLAN_H_INCLUDED_
diff --git a/magproto.cc b/magproto.cc
deleted file mode 100644 (file)
index b52beca..0000000
+++ /dev/null
@@ -1,1631 +0,0 @@
-/*
-    Communicate Thales/Magellan serial protocol.
-
-    Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007,
-      2008, 2010  Robert Lipe, robertlipe+source@gpsbabel.org
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-
- */
-
-#include <cassert>                 // for assert
-#include <cctype>                  // for isprint, toupper
-#include <cmath>                   // for fabs, lround
-#include <cstdio>                  // for sscanf, size_t
-#include <cstdlib>                 // for strtod, strtoul
-#include <cstring>                 // for strchr, strncmp, strlen, memmove, strrchr, memset
-
-#include <QByteArray>              // for QByteArray
-#include <QDateTime>               // for QDateTime
-#include <QDir>                    // for QDir, operator|, QDir::Files, QDir::Name, QDir::Readable
-#include <QFileInfo>               // for QFileInfo
-#include <QFileInfoList>           // for QFileInfoList
-#include <QLatin1String>           // for QLatin1String
-#include <QList>                   // for QList
-#include <QScopedPointer>          // for QScopedPointer
-#include <QString>                 // for QString, operator==
-#include <QStringList>             // for QStringList
-#include <QTime>                   // for QTime
-#include <QVector>                 // for QVector
-#include <Qt>                      // for CaseInsensitive
-#include <QtGlobal>                // for qPrintable, foreach
-
-#include "defs.h"
-#include "explorist_ini.h"         // for explorist_ini_done, explorist_ini_get, mag_info
-#include "format.h"                // for Format
-#include "gbfile.h"                // for gbfclose, gbfeof, gbfgets, gbfopen, gbfwrite, gbfile
-#include "gbser.h"                 // for gbser_deinit, gbser_init, gbser_is_serial, gbser_read_line, gbser_set_port, gbser_write, gbser_OK
-#include "magellan.h"              // for mm_meridian, mm_sportrak, magellan_icon_mapping_t, mm_gps315320, mm_unknown, mm_map330, mm_map410, pid_to_model_t, mm_gps310, m330_cleanse, mag_checksum, mag_find_descr_from_token, mag_find_token_from_descr, mag_rteparse, mag_trkparse
-#include "src/core/datetime.h"     // for DateTime
-#include "vecs.h"                  // for Vecs
-
-
-static int bitrate = 4800;
-static int wptcmtcnt;
-static int wptcmtcnt_max;
-static int explorist;
-static int broken_sportrak;
-#define MYNAME "MAGPROTO"
-#define MAXCMTCT 200
-
-#define debug_serial  (global_opts.debug_level > 1)
-
-static QString termread(char* ibuf, int size);
-static void termwrite(const char* obuf, int size);
-static void mag_readmsg(gpsdata_type objective);
-static void mag_handon();
-static void mag_handoff();
-static short_handle mkshort_handle = nullptr;
-static char* deficon = nullptr;
-static char* bs = nullptr;
-static char* cmts = nullptr;
-static char* noack = nullptr;
-static char* nukewpt = nullptr;
-static int route_out_count;
-static int waypoint_read_count;
-static int wpt_len = 8;
-static QString curfname;
-static int extension_hint;
-// For Explorist GC/510/610/710 families, bludgeon in GPX support.
-// (This has nothing to do with the Explorist 100...600 products.)
-static Format* gpx_vec;
-static mag_info* explorist_info;
-static QStringList os_gpx_files(const char* dirname);
-
-/*
- * Magellan's firmware is *horribly* slow to send the next packet after
- * we turn around an ack while we are reading from the device.  It's
- * quite spiffy when we're writing to the device.   Since we're *way*
- * less likely to lose data while reading from it than it is to lose data
- * when we write to it, we turn off the acks when we are predominantly
- * reading.
- */
-static int suppress_ack;
-
-enum mag_rxstate {
-  mrs_handoff = 0,
-  mrs_handon,
-  mrs_awaiting_ack
-};
-
-/*
- *   An individual element of a route.
- */
-struct mag_rte_elem {
-  QString wpt_name;
-  QString wpt_icon;
-};
-
-/*
- *  A header of a route.  Related elements of a route belong to this.
- */
-struct mag_rte_head_t {
-  QList<mag_rte_elem*> elem_list; /* list of child rte_elems */
-  char* rte_name{nullptr};
-  int nelems{0};
-};
-
-static QList<Waypoint*> rte_wpt_tmp; /* temporary PGMNWPL msgs for routes */
-
-static gbfile* magfile_h;
-static mag_rxstate magrxstate;
-static int mag_error;
-static unsigned int last_rx_csum;
-static int found_done;
-static int got_version;
-static int is_file = 0;
-static route_head* trk_head;
-static int ignore_unable;
-
-static Waypoint* mag_wptparse(char*);
-using cleanse_fn = QString (const char*);
-static cleanse_fn* mag_cleanse;
-static const char** os_get_magellan_mountpoints();
-
-static const magellan_icon_mapping_t gps315_icon_table[] = {
-  { "a", "filled circle" },
-  { "b", "box" },
-  { "c", "red buoy" },
-  { "d", "green buoy" },
-  { "e", "buoy" },
-  { "f", "rocks" },
-  { "g", "red daymark" },
-  { "h", "green daymark" },
-  { "i", "bell" },
-  { "j", "danger" },
-  { "k", "diver down" },
-  { "l", "fish" },
-  { "m", "house" },
-  { "n", "mark" },
-  { "o", "car" },
-  { "p", "tent" },
-  { "q", "boat" },
-  { "r", "food" },
-  { "s", "fuel" },
-  { "t", "tree" },
-  { nullptr, nullptr }
-};
-
-static const magellan_icon_mapping_t map330_icon_table[] = {
-  { "a", "crossed square" },
-  { "b", "box" },
-  { "c", "house" },
-  { "d", "aerial" },
-  { "e", "airport" },
-  { "f", "amusement park" },
-  { "g", "ATM" },
-  { "g", "Bank" },
-  { "h", "auto repair" },
-  { "i", "boating" },
-  { "j", "camping" },
-  { "k", "exit ramp" },
-  { "l", "first aid" },
-  { "m", "nav aid" },
-  { "n", "buoy" },
-  { "o", "fuel" },
-  { "p", "garden" },
-  { "q", "golf" },
-  { "r", "hotel" },
-  { "s", "hunting/fishing" },
-  { "t", "large city" },
-  { "u", "lighthouse" },
-  { "v", "major city" },
-  { "w", "marina" },
-  { "x", "medium city" },
-  { "y", "museum" },
-  { "z", "obstruction" },
-  { "aa", "park" },
-  { "ab", "resort" },
-  { "ac", "restaurant" },
-  { "ad", "rock" },
-  { "ae", "scuba" },
-  { "af", "RV service" },
-  { "ag", "shooting" },
-  { "ah", "sight seeing" },
-  { "ai", "small city" },
-  { "aj", "sounding" },
-  { "ak", "sports arena" },
-  { "al", "tourist info" },
-  { "am", "truck service" },
-  { "an", "winery" },
-  { "ao", "wreck" },
-  { "ap", "zoo" },
-  { "ah", "Virtual cache"},    /* Binos: because you "see" them. */
-  { "ak", "Micro-Cache" },     /* Looks like a film canister. */
-  { "an", "Multi-Cache"},      /* Winery: grapes 'coz they "bunch" */
-  { "s",  "Unknown Cache"},    /* 'Surprise' cache: use a target. */
-  { "ac",  "Event Cache"},     /* Event caches.  May be food. */
-  { nullptr, nullptr }
-};
-
-pid_to_model_t pid_to_model[] = {
-  { mm_gps315320, 19, "ColorTrak" },
-  { mm_gps315320, 24, "GPS 315/320" },
-  { mm_map410, 25, "Map 410" },
-  { mm_map330, 30, "Map 330" },
-  { mm_gps310, 31, "GPS 310" },
-  { mm_meridian, 33, "Meridian" },
-  { mm_meridian, 35, "ProMark 2" },
-  { mm_sportrak, 36, "SporTrak Map/Pro" },
-  { mm_sportrak, 37, "SporTrak" },
-  { mm_meridian, 38, "FX324 Plotter" },
-  { mm_meridian, 39, "Meridian Color" },
-  { mm_meridian, 40, "FX324C Plotter" },
-  { mm_sportrak, 41, "Sportrak Color" },
-  { mm_sportrak, 42, "Sportrak Marine" },
-  { mm_meridian, 43, "Meridian Marine" },
-  { mm_sportrak, 44, "Sportrak Topo" },
-  { mm_sportrak, 45, "Mystic" },
-  { mm_meridian, 46, "MobileMapper" },
-  { mm_meridian, 110, "Explorist 100" },
-  { mm_meridian, 111, "Explorist 200" },
-  { mm_unknown, 0, nullptr }
-};
-
-static const magellan_icon_mapping_t* icon_mapping = map330_icon_table;
-
-/*
- *   For each receiver type, return a "cleansed" version of the string
- *   that's valid for a waypoint name or comment.   The string should be
- *   freed when you're done with it.
- */
-static QString
-m315_cleanse(const char* istring)
-{
-  char* rstring = (char*) xmalloc(strlen(istring)+1);
-  char* o;
-  const char* i;
-  static char m315_valid_chars[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789";
-  for (o=rstring,i=istring; *i; i++) {
-    if (strchr(m315_valid_chars, toupper(*i))) {
-      *o++ = toupper(*i);
-    }
-  }
-  *o = 0;
-  QString rv(rstring);
-  xfree(rstring);
-  return rv;
-}
-
-/*
- * Do same for 330, Meridian, and SportTrak.
- */
-QString
-m330_cleanse(const char* istring)
-{
-  static char m330_valid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
-                                   "abcdefghijklmnopqrstuvwxyz"
-                                   "0123456789+-.'/!@#<%^&>()=:\\";
-  char* rstring = (char*) xmalloc(strlen(istring)+1);
-  char* o;
-  const char* i;
-
-  for (o=rstring,i=istring; *i; i++) {
-    if (strchr(m330_valid_chars, *i)) {
-      *o++ = *i;
-    }
-  }
-  *o = 0;
-  QString rv(rstring);
-  xfree(rstring);
-  return rv;
-}
-
-/*
- * Given a protocol message, compute the checksum as needed by
- * the Magellan protocol.
- */
-unsigned int
-mag_checksum(const char* const buf)
-{
-  int csum = 0;
-
-  for (const char* p = buf; *p; p++) {
-    csum  ^= *p;
-  }
-
-  return csum;
-}
-static unsigned int
-mag_pchecksum(const char* const buf, int len)
-{
-  int csum = 0;
-  const char* p = buf;
-  for (; len ; len--) {
-    csum ^= *p++;
-  }
-  return csum;
-}
-
-static void
-mag_writemsg(const char* const buf)
-{
-  unsigned int osum = mag_checksum(buf);
-  int retry_cnt = 5;
-  QScopedPointer<char, QScopedPointerPodDeleter> obuf;
-
-  if (debug_serial) {
-    warning("WRITE: $%s*%02X\r\n",buf, osum);
-  }
-
-retry:
-
-  int i = xasprintf(obuf, "$%s*%02X\r\n",buf, osum);
-  termwrite(obuf.data(), i);
-  if (magrxstate == mrs_handon || magrxstate == mrs_awaiting_ack) {
-    magrxstate = mrs_awaiting_ack;
-    mag_readmsg(trkdata);
-    if (last_rx_csum != osum) {
-      if (debug_serial) {
-        warning("COMM ERROR: Expected %02x, got %02x",
-                osum, last_rx_csum);
-      }
-      if (retry_cnt--) {
-        goto retry;
-      } else {
-        mag_handoff();
-        fatal(MYNAME
-              ": Too many communication errors.\n");
-      }
-    }
-  }
-}
-
-static void
-mag_writeack(int osum)
-{
-  QScopedPointer<char, QScopedPointerPodDeleter> nbuf;
-  QScopedPointer<char, QScopedPointerPodDeleter> obuf;
-
-  if (is_file) {
-    return;
-  }
-
-  (void) xasprintf(nbuf, "PMGNCSM,%02X", osum);
-  unsigned int nsum = mag_checksum(nbuf.data());
-  int i = xasprintf(obuf, "$%s*%02X\r\n",nbuf.data(), nsum);
-
-  if (debug_serial) {
-    warning("ACK WRITE: %s",obuf.data());
-  }
-  /*
-   * Don't call mag_writemsg here so we don't get into ack feedback
-   * loops.
-   */
-  termwrite(obuf.data(), i);
-}
-
-static void
-mag_handon()
-{
-  if (!is_file) {
-    mag_writemsg("PMGNCMD,HANDON");
-  }
-  magrxstate = mrs_handon;
-
-}
-
-static void
-mag_handoff()
-{
-  if (!is_file) {
-    mag_writemsg("PMGNCMD,HANDOFF");
-  }
-  magrxstate = mrs_handoff;
-}
-
-static void
-mag_verparse(char* ibuf)
-{
-  int prodid = mm_unknown;
-  char version[1024];
-  pid_to_model_t* pp = pid_to_model;
-
-  got_version = 1;
-  sscanf(ibuf,"$PMGNVER,%d,%[^,]", &prodid, version);
-
-  for (pp = pid_to_model; pp->model != mm_unknown; pp++) {
-    if (pp->pid == prodid) {
-      break;
-    }
-  }
-
-  if (prodid == 37) {
-    broken_sportrak = 1;
-  }
-
-  switch (pp->model) {
-  case mm_gps315320:
-  case mm_map410:
-    icon_mapping = gps315_icon_table;
-    setshort_length(mkshort_handle, 6);
-    setshort_mustupper(mkshort_handle, 1);
-    mag_cleanse = m315_cleanse;
-    break;
-  case mm_map330:
-  case mm_meridian:
-  case mm_sportrak:
-    icon_mapping = map330_icon_table;
-    setshort_length(mkshort_handle, wpt_len);
-    setshort_mustupper(mkshort_handle, 0);
-    mag_cleanse = m330_cleanse;
-    break;
-  default:
-    fatal(MYNAME ": Unknown receiver type %d, model version '%s'.\n", prodid, version);
-  }
-}
-
-#define IS_TKN(x) (strncmp(ibuf,x, sizeof(x)-1) == 0)
-
-static void
-mag_readmsg(gpsdata_type objective)
-{
-  char ibuf[512];      /* oliskoli: corrupted data (I've seen descr with a lot
-                                    of escaped FFFFFFFF) may need more size  */
-  int retrycnt = 20;
-
-retry:
-  QString gr = termread(ibuf, sizeof(ibuf));
-
-  if (gr.isEmpty()) {
-    if (!got_version) {
-      /*
-       * The 315 can take up to six seconds to respond to
-       * a VERSION command.   Since this is on startup,
-       * we'll be fairly persistent in retrying.
-       */
-      if (retrycnt--) {
-        goto retry;
-      } else {
-        fatal(MYNAME ": No data received from GPS.\n");
-      }
-    } else {
-      if (is_file)  {
-        found_done = 1;
-      }
-      return;
-    }
-  }
-
-  /* If column zero isn't a dollar sign, it's not for us */
-  if (ibuf[0] != '$') {
-    fatal(MYNAME ": line doesn't start with '$'.\n");
-  }
-
-
-  int isz = strlen(ibuf);
-
-  if (isz < 5) {
-    if (debug_serial) {
-      warning("SHORT READ %d\n", isz);
-    }
-    return;
-  }
-  mag_error = 0;
-  while (!isprint(ibuf[isz])) {
-    isz--;
-  }
-  char* isump = &ibuf[isz-1];
-  unsigned int isum = strtoul(isump, nullptr,16);
-  if (isum != mag_pchecksum(&ibuf[1], isz-3)) {
-    if (debug_serial) {
-      warning("RXERR %02x/%02x: '%s'\n", isum, mag_pchecksum(&ibuf[1],isz-5), ibuf);
-    }
-    /* Special case receive errors early on. */
-    if (!got_version) {
-      fatal(MYNAME ": bad communication.  Check bit rate.\n");
-    }
-  }
-  if (debug_serial) {
-    warning("READ: %s\n", ibuf);
-  }
-  if (IS_TKN("$PMGNCSM,")) {
-    last_rx_csum = strtoul(&ibuf[9], nullptr, 16);
-    magrxstate = mrs_handon;
-    return;
-  }
-  if (strncmp(ibuf, "$PMGNWPL,", 7) == 0) {
-    Waypoint* wpt = mag_wptparse(ibuf);
-    waypoint_read_count++;
-    if (global_opts.verbose_status) {
-      waypt_status_disp(waypoint_read_count,
-                        waypoint_read_count);
-    }
-
-    if (extension_hint) {
-      if (extension_hint == WPTDATAMASK) {
-        waypt_add(wpt);
-      } else if (extension_hint == RTEDATAMASK) {
-        rte_wpt_tmp.append(wpt);
-      }
-    } else {
-      switch (objective) {
-      case wptdata:
-        waypt_add(wpt);
-        break;
-      case rtedata:
-        rte_wpt_tmp.append(wpt);
-        break;
-      default:
-        break;
-      }
-    }
-  }
-  if (strncmp(ibuf, "$PMGNTRK,", 7) == 0) {
-    Waypoint* wpt = mag_trkparse(ibuf);
-    /*
-     * Allow lazy allocation of track head.
-     */
-    if (trk_head == nullptr) {
-      /* These tracks don't have names, so derive one
-       * from input filename.
-       */
-
-      trk_head = new route_head;
-
-      /* Whack trailing extension if present. */
-      QString s = get_filename(curfname);
-      int idx = s.indexOf('.');
-      if (idx > 0) {
-        s.truncate(idx);
-      }
-
-      trk_head->rte_name = s;
-      track_add_head(trk_head);
-    }
-
-    track_add_wpt(trk_head, wpt);
-  }
-  if (strncmp(ibuf, "$PMGNRTE,", 7) == 0) {
-    mag_rteparse(ibuf);
-  }
-  if (IS_TKN("$PMGNVER,")) {
-    mag_verparse(ibuf);
-  }
-  mag_error = 0;
-  if (!ignore_unable && IS_TKN("$PMGNCMD,UNABLE")) {
-    warning("Unable to send\n");
-    found_done = 1;
-    mag_error = 1;
-    ignore_unable = 0;
-    return;
-  }
-  if (IS_TKN("$PMGNCMD,END") || (is_file && (gbfeof(magfile_h)))) {
-    found_done = 1;
-    return;
-  }
-
-  if (magrxstate != mrs_handoff) {
-    mag_writeack(isum);
-  }
-}
-
-static void* serial_handle = nullptr;
-
-static int
-terminit(const QString& portname, int create_ok)
-{
-  if (gbser_is_serial(qPrintable(portname))) {
-    if (serial_handle = gbser_init(qPrintable(portname)), nullptr != serial_handle) {
-      int rc;
-      if (rc = gbser_set_port(serial_handle, bitrate, 8, 0, 1), gbser_OK != rc) {
-        fatal(MYNAME ": Can't configure port\n");
-      }
-    }
-    is_file = 0;
-    if (serial_handle == nullptr) {
-      fatal(MYNAME ": Could not open serial port %s\n", qPrintable(portname));
-    }
-    return 1;
-  } else {
-    /* Does this check for an error? */
-    magfile_h = gbfopen(portname, create_ok ? "w+b" : "rb", MYNAME);
-    is_file = 1;
-    icon_mapping = map330_icon_table;
-    mag_cleanse = m330_cleanse;
-    got_version = 1;
-    return 0;
-  }
-}
-
-static QString termread(char* ibuf, int size)
-{
-  if (is_file) {
-    return gbfgets(ibuf, size, magfile_h);
-  } else {
-    int rc = gbser_read_line(serial_handle, ibuf, size, 2000, 0x0a, 0x0d);
-    if (rc != gbser_OK) {
-      fatal(MYNAME ": Read error\n");
-    }
-    return ibuf;
-  }
-}
-
-/* Though not documented in the protocol spec, if the unit itself
- * wants to create a field containing a comma, it will encode it
- * as <escape>2C.  We extrapolate that any 2 digit hex encoding may
- * be valid.  We don't do this in termread() since we need to do it
- * after the scanf.  This means we have to do it field-by-field
- * basis.
- *
- * The buffer is modified in place and shortened by copying the remaining
- * string including the terminator.
- */
-static
-void
-mag_dequote(char* ibuf)
-{
-  char* esc = nullptr;
-
-  while ((esc = strchr(ibuf, 0x1b))) {
-    int nremains = strlen(esc);
-    if (nremains >= 3) {
-      static const char hex[17] = "0123456789ABCDEF";
-      const char* c1 = strchr(hex, esc[1]);
-      const char* c2 = strchr(hex, esc[2]);
-      if (c1 && c2) {
-        int escv = (c1 - hex) * 16 + (c2 - hex);
-        if (escv == 255) {     /* corrupted data */
-          char* tmp = esc + 1;
-          while (*tmp == 'F') {
-            tmp++;
-          }
-          memmove(esc, tmp, strlen(tmp) + 1);
-        } else {
-          *esc++ = (isprint(escv)) ? escv : '$';
-          /* buffers overlap */
-          memmove(esc, esc+2, nremains - 2);
-        }
-      }
-    } else {
-      *esc = '\0';     /* trim corrupted data,
-                                          otherwise we get an endless loop */
-    }
-  }
-}
-
-static void
-termwrite(const char* obuf, int size)
-{
-  if (is_file) {
-    size_t nw;
-    if (nw = gbfwrite(obuf, 1, size, magfile_h), nw < (size_t) size) {
-      fatal(MYNAME ": Write error");
-    }
-  } else {
-    int rc;
-    if (rc = gbser_write(serial_handle, obuf, size), rc < 0) {
-      fatal(MYNAME ": Write error");
-    }
-  }
-}
-
-static void termdeinit()
-{
-  if (is_file) {
-    gbfclose(magfile_h);
-    magfile_h = nullptr;
-  } else {
-    gbser_deinit(serial_handle);
-    serial_handle = nullptr;
-  }
-}
-
-/*
- *  Arg tables are doubled up so that -? can output appropriate help
- */
-static
-QVector<arglist_t> mag_sargs = {
-  {
-    "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING,
-    ARG_NOMINMAX, nullptr
-  },
-  {
-    "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)",
-    "200", ARGTYPE_INT, ARG_NOMINMAX, nullptr
-  },
-  {
-    "baud", &bs, "Numeric value of bitrate (baud=4800)", "4800",
-    ARGTYPE_INT, ARG_NOMINMAX, nullptr
-  },
-  {
-    "noack", &noack, "Suppress use of handshaking in name of speed",
-    nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
-  },
-  {
-    "nukewpt", &nukewpt, "Delete all waypoints", nullptr, ARGTYPE_BOOL,
-    ARG_NOMINMAX, nullptr
-  },
-};
-
-static
-QVector<arglist_t> mag_fargs = {
-  {
-    "deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING,
-    ARG_NOMINMAX, nullptr
-  },
-  {
-    "maxcmts", &cmts, "Max number of comments to write (maxcmts=200)",
-    nullptr, ARGTYPE_INT, ARG_NOMINMAX, nullptr
-  },
-};
-
-/*
- * The part of the serial init that's common to read and write.
- */
-static void
-mag_serial_init_common(const QString& portname)
-{
-  if (is_file) {
-    return;
-  }
-
-  mag_handoff();
-  if (!noack && !suppress_ack) {
-    mag_handon();
-  }
-
-  time_t now = current_time().toTime_t();
-  /*
-   * The 315 can take up to 4.25 seconds to respond to initialization
-   * commands.   Time out on the side of caution.
-   */
-  time_t later = now + 6;
-  got_version = 0;
-  mag_writemsg("PMGNCMD,VERSION");
-
-  while (!got_version) {
-    mag_readmsg(trkdata);
-    if (current_time().toTime_t() > later) {
-      fatal(MYNAME ": No acknowledgment from GPS on %s\n",
-            qPrintable(portname));
-    }
-  }
-
-  if ((icon_mapping != gps315_icon_table)) {
-    /*
-     * The 315 can't handle this command, so we set a global
-     * to ignore the NAK on it.
-     */
-    ignore_unable = 1;
-    mag_writemsg("PMGNCMD,NMEAOFF");
-    ignore_unable = 0;
-  }
-
-  if (nukewpt) {
-    /* The unit will send us an "end" message upon completion */
-    mag_writemsg("PMGNCMD,DELETE,WAYPOINT");
-    mag_readmsg(trkdata);
-    if (!found_done) {
-      fatal(MYNAME ": Unexpected response to waypoint delete command.\n");
-    }
-    found_done = 0;
-  }
-
-}
-static void
-mag_rd_init_common(const QString& portname)
-{
-  waypoint_read_count = 0;
-  // For Explorist GC, intercept the device access and redirect to GPX.
-  // We actually do the rd_init() inside read as we may have multiple
-  // files that we have to read.
-  if (portname == "usb:") {
-    const char** dlist = os_get_magellan_mountpoints();
-    explorist_info = explorist_ini_get(dlist);
-    if (explorist_info) {
-      gpx_vec = Vecs::Instance().find_vec("gpx");
-    }
-    return;
-  }
-
-  if (bs) {
-    bitrate=xstrtoi(bs, nullptr, 10);
-  }
-
-  if (!mkshort_handle) {
-    mkshort_handle = mkshort_new_handle();
-  }
-
-  terminit(portname, 0);
-  mag_serial_init_common(portname);
-
-  rte_wpt_tmp.clear();
-
-  /* find the location of the tail of the path name,
-   * make a copy of it, then lop off the file extension
-   */
-
-  curfname = get_filename(portname);
-
-  /*
-   * I'd rather not derive behaviour from filenames but since
-   * we can't otherwise tell if we should put a WPT on the route
-   * queue or the WPT queue in the presence of (-w -r -t) we
-   * divine a hint from the filename extension when we can.
-   */
-  QString exten = QFileInfo(curfname).suffix();
-  if (exten.length() > 0) {
-    if (0 == exten.compare(QLatin1String("upt"), Qt::CaseInsensitive)) {
-      extension_hint = WPTDATAMASK;
-    } else if (0 == exten.compare(QLatin1String("log"), Qt::CaseInsensitive)) {
-      extension_hint = TRKDATAMASK;
-    } else if (0 == exten.compare(QLatin1String("rte"), Qt::CaseInsensitive)) {
-      extension_hint = RTEDATAMASK;
-    }
-  }
-
-}
-
-static void
-mag_rd_init(const QString& portname)
-{
-  explorist = 0;
-  suppress_ack = 1;
-  mag_rd_init_common(portname);
-}
-
-static void
-magX_rd_init(const QString& portname)
-{
-  explorist = 1;
-  mag_rd_init_common(portname);
-}
-
-static void
-mag_wr_init_common(const QString& portname)
-{
-  suppress_ack = 0;
-  if (bs) {
-    bitrate=xstrtoi(bs, nullptr, 10);
-  }
-
-  if (waypt_count() > 500) {
-    fatal(MYNAME ": Meridian/Explorist does not support more than 500 waypoints in one file. Only\n200 waypoints may have comments.\nDecrease the number of waypoints sent.\n");
-  }
-
-  if (cmts) {
-    wptcmtcnt_max = xstrtoi(cmts, nullptr, 10);
-  } else {
-    wptcmtcnt_max = MAXCMTCT ;
-  }
-
-  if (!mkshort_handle) {
-    mkshort_handle = mkshort_new_handle();
-  }
-
-  terminit(portname, 1);
-  mag_serial_init_common(portname);
-
-  rte_wpt_tmp.clear();
-}
-
-/*
- * Entry point for extended (explorist) points.
- */
-static void
-magX_wr_init(const QString& portname)
-{
-  wpt_len = 20;
-  explorist = 1;
-  mag_wr_init_common(portname);
-  setshort_length(mkshort_handle, wpt_len);
-  setshort_whitespace_ok(mkshort_handle, 1);
-}
-
-static void
-mag_wr_init(const QString& portname)
-{
-  explorist = 0;
-  wpt_len = 8;
-  mag_wr_init_common(portname);
-  /*
-   * Whitespace is actually legal, but since waypoint name length is
-   * only 8 bytes, we'll conserve them.
-   */
-
-  setshort_whitespace_ok(mkshort_handle, 0);
-}
-
-static void
-mag_deinit()
-{
-  if (explorist_info) {
-    explorist_ini_done(explorist_info);
-    return;
-  }
-  mag_handoff();
-  termdeinit();
-  if (mkshort_handle) {
-    mkshort_del_handle(&mkshort_handle);
-  }
-
-  while (!rte_wpt_tmp.isEmpty()) {
-    delete rte_wpt_tmp.takeFirst();
-  }
-
-  trk_head = nullptr;
-
-  curfname.clear();
-}
-
-static void
-mag_wr_deinit()
-{
-  if (explorist) {
-    mag_writemsg("PMGNCMD,END");
-  }
-  mag_deinit();
-}
-
-/*
- * I'm tired of arguing with scanf about optional fields .  Detokenize
- * an incoming string that may contain empty fields.
- *
- * Probably should be cleaned up and moved to common code, but
- * making it deal with an arbitrary number of fields of arbitrary
- * size is icky.  We don't have to solve the general case here...
- */
-
-static char ifield[20][100];
-static
-void parse_istring(char* istring)
-{
-  int f = 0;
-  int n;
-  while (istring[0]) {
-    char* fp = ifield[f];
-    int x = sscanf(istring, "%[^,]%n", fp, &n);
-    f++;
-    if (x) {
-      istring += n;
-      /* IF more in this string, skip delim */
-      if (istring[0]) {
-        istring++;
-      }
-    } else {
-      istring ++;
-    }
-  }
-}
-
-/*
- * Given an incoming track messages of the form:
- * $PMGNTRK,3605.259,N,08644.389,W,00151,M,201444.61,A,,020302*66
- * create and return a populated waypoint.
- */
-Waypoint*
-mag_trkparse(char* trkmsg)
-{
-  int hms;
-  int fracsecs;
-  struct tm tm;
-
-  auto* waypt = new Waypoint;
-
-  memset(&tm, 0, sizeof(tm));
-
-  /*
-   * As some of the fields are optional, sscanf works badly
-   * for us.
-   */
-  parse_istring(trkmsg);
-  double latdeg = strtod(ifield[1], nullptr);
-  char latdir = ifield[2][0];
-  double lngdeg = strtod(ifield[3], nullptr);
-  char lngdir = ifield[4][0];
-  int alt = strtod(ifield[5], nullptr);
-  char altunits = ifield[6][0];
-  (void)altunits;
-  sscanf(ifield[7], "%d.%d", &hms, &fracsecs);
-  /* Field 8 is constant */
-  /* Field nine is optional track name */
-  int dmy = xstrtoi(ifield[10], nullptr, 10);
-  int sec = hms % 100;
-  hms = hms / 100;
-  int min = hms % 100;
-  hms = hms / 100;
-  int hour = hms % 100;
-
-  int year = 100 + dmy % 100 + 1900;
-  dmy = dmy / 100;
-  int mon =  dmy % 100;
-  dmy = dmy / 100;
-  int day = dmy % 100;
-  QDateTime dt(QDate(year, mon, day), QTime(hour, min, sec, fracsecs * 10), Qt::UTC);
-  waypt->SetCreationTime(dt);
-
-  if (latdir == 'S') {
-    latdeg = -latdeg;
-  }
-  waypt->latitude = ddmm2degrees(latdeg);
-
-  if (lngdir == 'W') {
-    lngdeg = -lngdeg;
-  }
-  waypt->longitude = ddmm2degrees(lngdeg);
-
-  waypt->altitude = alt;
-
-  return waypt;
-
-}
-
-/*
- * Given an incoming route messages of the form:
- * $PMGNRTE,4,1,c,1,DAD,a,Anna,a*61
- * generate a route.
- */
-void
-mag_rteparse(char* rtemsg)
-{
-  int n;
-  int frags,frag,rtenum;
-  char xbuf[100],next_stop[100],abuf[100];
-  char* currtemsg;
-  static mag_rte_head_t* mag_rte_head;
-
-#if 0
-  sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n",
-         &frags,&frag,xbuf,&rtenum,&n);
-#else
-  sscanf(rtemsg,"$PMGNRTE,%d,%d,%c,%d%n",
-         &frags,&frag,xbuf,&rtenum,&n);
-
-  /* Explorist has a route name here */
-  QString rte_name;
-  if (explorist) {
-    char* ca = rtemsg + n;
-    if (*ca++ != ',') {
-      fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg);
-    }
-
-    char* ce = strchr(ca, ',');
-    if (ce == nullptr) {
-      fatal(MYNAME ": Incorrectly formatted route line '%s'", rtemsg);
-    }
-
-    if (ca == ce) {
-      rte_name = "Route";
-      rte_name += QString::number(rtenum);
-    } else {
-      rte_name = ca;
-      rte_name.truncate(ce-ca);
-    }
-
-    n += ((ce - ca) + 1);
-  }
-
-#endif
-
-  /*
-   * This is the first component of a route.  Allocate a new
-   * head.
-   */
-  if (frag == 1) {
-    mag_rte_head = new mag_rte_head_t;
-    mag_rte_head->nelems = frags;
-  }
-
-  currtemsg = rtemsg + n;
-
-  /*
-   * The individual line may contain several route elements.
-   * loop and pick those up.
-   */
-  while (sscanf(currtemsg,",%[^,],%[^,]%n",next_stop, abuf,&n)) {
-    if ((next_stop[0] == 0) || (next_stop[0] == '*')) {
-      break;
-    }
-
-    /* trim CRC from waypoint icon string */
-    if (char* p = strchr(abuf, '*'); p != nullptr) {
-      *p = '\0';
-    }
-
-    auto* rte_elem = new mag_rte_elem;
-
-    rte_elem->wpt_name = next_stop;
-    rte_elem->wpt_icon = abuf;
-
-    mag_rte_head->elem_list.append(rte_elem);
-
-    /* Sportrak (the non-mapping unit) creates malformed
-     * RTE sentence with no icon info after the routepoint
-     * name.  So if we saw an "icon" treat that as new
-     * routepoint.
-     */
-    if (broken_sportrak && abuf[0]) {
-      rte_elem = new mag_rte_elem;
-      rte_elem->wpt_name = abuf;
-
-      mag_rte_head->elem_list.append(rte_elem);
-    }
-
-    next_stop[0] = 0;
-    currtemsg += n;
-  }
-
-  /*
-   * If this was the last fragment of the route, add it to the
-   * gpsbabel internal structs now.
-   */
-  if (frag == mag_rte_head->nelems) {
-
-    auto* rte_head = new route_head;
-    route_add_head(rte_head);
-    rte_head->rte_num = rtenum;
-    rte_head->rte_name = rte_name;
-
-    /*
-     * It is quite feasible that we have 200 waypoints,
-     * 3 of which are used in the route.  We'll need to find
-     * those in the queue for SD routes...
-     */
-
-    while (!mag_rte_head->elem_list.isEmpty()) {
-      mag_rte_elem* re = mag_rte_head->elem_list.takeFirst();
-
-      /*
-       * Copy route points from temp wpt queue.
-       */
-      foreach (const Waypoint* waypt, rte_wpt_tmp) {
-        if (waypt->shortname == re->wpt_name) {
-          auto* wpt = new Waypoint(*waypt);
-          route_add_wpt(rte_head, wpt);
-          break;
-        }
-      }
-
-      delete re;
-    }
-    delete mag_rte_head;
-  }
-}
-
-QString
-mag_find_descr_from_token(const char* token)
-{
-  if (icon_mapping == nullptr) {
-    return "unknown";
-  }
-
-  for (const magellan_icon_mapping_t* i = icon_mapping; i->token; i++) {
-    if (token[0] == 0) {
-      break;
-    }
-    if (case_ignore_strcmp(token, i->token) == 0) {
-      return i->icon;
-    }
-  }
-  return icon_mapping[0].icon;
-}
-
-QString
-mag_find_token_from_descr(const QString& icon)
-{
-  const magellan_icon_mapping_t* i = icon_mapping;
-
-  if (i == nullptr || icon == nullptr) {
-    return "a";
-  }
-
-  for (i = icon_mapping; i->token; i++) {
-    if (icon.compare(i->icon, Qt::CaseInsensitive) == 0) {
-      return i->token;
-    }
-  }
-  return icon_mapping[0].token;
-}
-
-/*
- * Given an incoming waypoint messages of the form:
- * $PMGNWPL,3549.499,N,08650.827,W,0000257,M,HOME,HOME,c*4D
- * create and return a populated waypoint.
- */
-static Waypoint*
-mag_wptparse(char* trkmsg)
-{
-  double latdeg, lngdeg;
-  char latdir;
-  char lngdir;
-  int alt;
-  char altunits;
-  char shortname[100];
-  char descr[256];
-  char icon_token[100];
-  int i = 0;
-
-  descr[0] = 0;
-  icon_token[0] = 0;
-
-  auto* waypt = new Waypoint;
-
-  sscanf(trkmsg,"$PMGNWPL,%lf,%c,%lf,%c,%d,%c,%[^,],%[^,]",
-         &latdeg,&latdir,
-         &lngdeg,&lngdir,
-         &alt,&altunits,shortname,descr);
-  char* icone = strrchr(trkmsg, '*');
-  char* icons = strrchr(trkmsg, ',')+1;
-
-  mag_dequote(descr);
-
-  for (char* blah = icons ; blah < icone; blah++) {
-    icon_token[i++] = *blah;
-  }
-  icon_token[i++] = '\0';
-
-  if (latdir == 'S') {
-    latdeg = -latdeg;
-  }
-  waypt->latitude = ddmm2degrees(latdeg);
-
-  if (lngdir == 'W') {
-    lngdeg = -lngdeg;
-  }
-  waypt->longitude = ddmm2degrees(lngdeg);
-
-  waypt->altitude = alt;
-  waypt->shortname = shortname;
-  waypt->description = descr;
-  waypt->icon_descr = mag_find_descr_from_token(icon_token);
-
-  return waypt;
-}
-
-static void
-mag_read()
-{
-  if (gpx_vec) {
-    QStringList f = os_gpx_files(explorist_info->track_path);
-    for (const auto& file : qAsConst(f)) {
-      gpx_vec->rd_init(file);
-      gpx_vec->read();
-      gpx_vec->rd_deinit();
-    }
-
-    f = os_gpx_files(explorist_info->waypoint_path);
-    for (const auto& file : qAsConst(f)) {
-      gpx_vec->rd_init(file);
-      gpx_vec->read();
-      gpx_vec->rd_deinit();
-    }
-#if 0
-    f = os_gpx_files(explorist_info->geo_path);
-    for (const auto& file : qAsConst(f)) {
-      gpx_vec->rd_init(file);
-      gpx_vec->read();
-      gpx_vec->rd_deinit();
-    }
-#endif
-    return;
-  }
-
-  found_done = 0;
-  if (global_opts.masked_objective & TRKDATAMASK) {
-    magrxstate = mrs_handoff;
-    if (!is_file) {
-      mag_writemsg("PMGNCMD,TRACK,2");
-    }
-
-    while (!found_done) {
-      mag_readmsg(trkdata);
-    }
-  }
-
-  found_done = 0;
-  if (global_opts.masked_objective & WPTDATAMASK) {
-    magrxstate = mrs_handoff;
-    if (!is_file) {
-      mag_writemsg("PMGNCMD,WAYPOINT");
-    }
-
-    while (!found_done) {
-      mag_readmsg(wptdata);
-    }
-  }
-
-  found_done = 0;
-  if (global_opts.masked_objective & RTEDATAMASK) {
-    magrxstate = mrs_handoff;
-    if (!is_file) {
-      /*
-       * serial routes require waypoint & routes
-       * messages commands.
-       */
-      mag_writemsg("PMGNCMD,WAYPOINT");
-
-      while (!found_done) {
-        mag_readmsg(rtedata);
-      }
-
-      mag_writemsg("PMGNCMD,ROUTE");
-
-      found_done = 0;
-      while (!found_done) {
-        mag_readmsg(rtedata);
-      }
-    } else {
-      /*
-       * SD routes are a stream of PMGNWPL and
-       * PMGNRTE messages, in that order.
-       */
-      while (!found_done) {
-        mag_readmsg(rtedata);
-      }
-    }
-  }
-}
-
-static
-void
-mag_waypt_pr(const Waypoint* waypointp)
-{
-  QScopedPointer<char, QScopedPointerPodDeleter> obuf;
-  QScopedPointer<char, QScopedPointerPodDeleter> ofmtdesc;
-  QString icon_token;
-
-  double ilat = waypointp->latitude;
-  double ilon = waypointp->longitude;
-
-  double lon = fabs(ilon);
-  double lat = fabs(ilat);
-
-  int lon_deg = lon;
-  int lat_deg = lat;
-
-  lon = (lon - lon_deg) * 60.0;
-  lat = (lat - lat_deg) * 60.0;
-
-  lon = (lon_deg * 100.0 + lon);
-  lat = (lat_deg * 100.0 + lat);
-
-  if (deficon)  {
-    icon_token = mag_find_token_from_descr(deficon);
-  } else {
-    icon_token = mag_find_token_from_descr(waypointp->icon_descr);
-  }
-
-  if (!get_cache_icon(waypointp).isEmpty()) {
-    icon_token = mag_find_token_from_descr(get_cache_icon(waypointp));
-  }
-
-  QString isrc = waypointp->notes.isEmpty() ? waypointp->description : waypointp->notes;
-  QString owpt = global_opts.synthesize_shortnames ?
-         mkshort_from_wpt(mkshort_handle, waypointp) : waypointp->shortname;
-  QString odesc = isrc;
-  owpt = mag_cleanse(CSTRc(owpt));
-
-  if (global_opts.smart_icons &&
-      waypointp->gc_data->diff && waypointp->gc_data->terr) {
-    // It's a string and compactness counts, so "1.0" is OK to be "10".
-    xasprintf(ofmtdesc, "%ud/%ud %s", waypointp->gc_data->diff,
-            waypointp->gc_data->terr, CSTRc(odesc));
-    odesc = mag_cleanse(ofmtdesc.data());
-  } else {
-    odesc = mag_cleanse(CSTRc(odesc));
-  }
-
-  /*
-   * For the benefit of DirectRoute (which uses waypoint comments
-   * to deliver turn-by-turn popups for street routing) allow a
-   * cap on the comments delivered so we leave space for it to route.
-   */
-  if (!odesc.isEmpty() && (wptcmtcnt++ >= wptcmtcnt_max)) {
-    odesc.clear();
-  }
-
-  xasprintf(obuf, "PMGNWPL,%4.3f,%c,%09.3f,%c,%07.0f,M,%-.*s,%-.46s,%s",
-          lat, ilat < 0 ? 'S' : 'N',
-          lon, ilon < 0 ? 'W' : 'E',
-          waypointp->altitude == unknown_alt ?
-          0 : waypointp->altitude,
-          wpt_len,
-          CSTRc(owpt),
-          CSTRc(odesc),
-          CSTR(icon_token));
-  mag_writemsg(obuf.data());
-
-  if (!is_file) {
-    if (mag_error) {
-      warning("Protocol error Writing '%s'\n", obuf.data());
-    }
-  }
-}
-
-static
-void mag_track_disp(const Waypoint* waypointp)
-{
-  QScopedPointer<char, QScopedPointerPodDeleter> obuf;
-
-  double ilat = waypointp->latitude;
-  double ilon = waypointp->longitude;
-
-  QByteArray dmy("");
-  QByteArray hms("");
-  if (waypointp->creation_time.isValid()) {
-    // Round to hundredths of seconds before conversion to string.
-    // Rounding can ripple all the way from the msec to the year.
-    QDateTime dt = waypointp->GetCreationTime().toUTC();
-    dt = dt.addMSecs(10 * lround(dt.time().msec()/10.0) - dt.time().msec());
-    assert((dt.time().msec() % 10) == 0);
-    dmy = dt.toString(u"ddMMyy").toUtf8();
-    hms = dt.toString(u"hhmmss.zzz").left(9).toUtf8();
-  }
-
-  double lon = fabs(ilon);
-  double lat = fabs(ilat);
-
-  int lon_deg = lon;
-  int lat_deg = lat;
-
-  lon = (lon - lon_deg) * 60.0;
-  lat = (lat - lat_deg) * 60.0;
-
-  lon = (lon_deg * 100.0 + lon);
-  lat = (lat_deg * 100.0 + lat);
-
-  xasprintf(obuf,"PMGNTRK,%4.3f,%c,%09.3f,%c,%05.0f,%c,%s,A,,%s",
-          lat, ilat < 0 ? 'S' : 'N',
-          lon, ilon < 0 ? 'W' : 'E',
-          waypointp->altitude == unknown_alt ?
-          0 : waypointp->altitude,
-          'M', hms.constData(), dmy.constData());
-  mag_writemsg(obuf.data());
-}
-
-static
-void mag_track_pr()
-{
-  track_disp_all(nullptr, nullptr, mag_track_disp);
-}
-
-/*
-The spec says to stack points:
-       $PMGNRTE,2,1,c,1,FOO,POINT1,b,POINT2,c,POINT3,d*6C<CR><LF>
-
-Meridian SD card and serial (at least) writes in pairs:
-       $PMGNRTE,4,1,c,1,HOME,c,I49X73,a*15
-       ...
-       $PMGNRTE,4,4,c,1,RON273,a,MYCF93,a*7B
-
-The spec also says that some units don't like single-legged pairs,
-and to replace the 2nd name with "<<>>", but I haven't seen one of those.
-*/
-
-static void
-mag_route_trl(const route_head* rte)
-{
-  QScopedPointer<char, QScopedPointerPodDeleter> obuff;
-  QString buff1;
-  QString buff2;
-  QString icon_token;
-
-  /* count waypoints for this route */
-  int i = rte->rte_waypt_ct();
-
-  /* number of output PMGNRTE messages at 2 points per line */
-  int numlines = (i / 2) + (i % 2);
-
-  /* increment the route counter. */
-  route_out_count++;
-
-  int thisline = i = 0;
-  foreach (const Waypoint* waypointp, rte->waypoint_list) {
-    i++;
-
-    if (deficon) {
-      icon_token = mag_find_token_from_descr(deficon);
-    } else {
-      icon_token = mag_find_token_from_descr(waypointp->icon_descr);
-    }
-
-    QString* pbuff;
-    if (i == 1) {
-      pbuff = &buff1;
-    } else {
-      pbuff = &buff2;
-    }
-    // Write name, icon tuple into alternating buff1/buff2 buffer.
-    *pbuff = waypointp->shortname + ',' + icon_token;
-
-    if ((waypointp == rte->waypoint_list.back()) || ((i % 2) == 0)) {
-      QString expbuf;
-      thisline++;
-      if (explorist) {
-        expbuf = rte->rte_name + ',';
-      }
-
-      xasprintf(obuff, "PMGNRTE,%d,%d,c,%d,%s%s,%s",
-              numlines, thisline,
-              rte->rte_num ? rte->rte_num : route_out_count,
-              CSTRc(expbuf),
-              CSTR(buff1), CSTR(buff2));
-
-      mag_writemsg(obuff.data());
-      buff1.clear();
-      buff2.clear();
-      i = 0;
-    }
-  }
-}
-
-static void
-mag_route_pr()
-{
-  route_out_count = 0;
-  route_disp_all(nullptr, mag_route_trl, mag_waypt_pr);
-
-}
-
-static void
-mag_write()
-{
-
-  wptcmtcnt = 0;
-
-  switch (global_opts.objective) {
-  case trkdata:
-    mag_track_pr();
-    break;
-  case wptdata:
-    waypt_disp_all(mag_waypt_pr);
-    break;
-  case rtedata:
-    mag_route_pr();
-    break;
-  default:
-    fatal(MYNAME ": Unknown objective.\n");
-  }
-}
-
-const char** os_get_magellan_mountpoints()
-{
-#if __APPLE__
-  const char** dlist = (const char**) xcalloc(2, sizeof *dlist);
-  dlist[0] = xstrdup("/Volumes/Magellan");
-  dlist[1] = nullptr;
-  return dlist;
-#else
-  fatal("Not implemented");
-  return nullptr;
-#endif
-}
-
-static QStringList
-os_gpx_files(const char* dirname)
-{
-  QDir dir(dirname);
-
-  const QFileInfoList filist = dir.entryInfoList(QStringList("*.gpx"), QDir::Files | QDir::Readable, QDir::Name);
-  QStringList rv;
-  for (const auto& fi : filist) {
-    rv.append(fi.absoluteFilePath());
-  }
-  return rv;
-}
-
-/*
- *  This is repeated just so it shows up as separate menu options
- *  for the benefit of GUI wrappers.
- */
-ff_vecs_t mag_svecs = {
-  ff_type_serial,
-  FF_CAP_RW_ALL,
-  mag_rd_init,
-  mag_wr_init,
-  mag_deinit,
-  mag_deinit,
-  mag_read,
-  mag_write,
-  nullptr,
-  &mag_sargs,
-  CET_CHARSET_ASCII, 0,        /* CET-REVIEW */
-  NULL_POS_OPS
-};
-
-ff_vecs_t mag_fvecs = {
-  ff_type_file,
-  FF_CAP_RW_ALL,
-  mag_rd_init,
-  mag_wr_init,
-  mag_deinit,
-  mag_deinit,
-  mag_read,
-  mag_write,
-  nullptr,
-  &mag_fargs,
-  CET_CHARSET_ASCII, 0,        /* CET-REVIEW */
-  NULL_POS_OPS
-};
-
-/*
- * Extended (Explorist) entry tables.
- */
-ff_vecs_t magX_fvecs = {
-  ff_type_file,
-  FF_CAP_RW_ALL,
-  magX_rd_init,
-  magX_wr_init,
-  mag_deinit,
-  mag_wr_deinit,
-  mag_read,
-  mag_write,
-  nullptr,
-  &mag_fargs,
-  CET_CHARSET_ASCII, 0,        /* CET-REVIEW */
-  NULL_POS_OPS
-};
index 573facd3c598b6e57a994619a593cdd317066709..27c10e6ec0b9cbd292132d8af03a013f0e2f5f43 100644 (file)
@@ -29,7 +29,7 @@ plot "-" with lines
 99.9720        905.512
 100.1892       902.231
 100.5724       882.546
-102.0104       882.546
+102.0103       882.546
 102.8041       875.984
 103.0804       872.703
 107.3975       921.916
diff --git a/reference/dusky.gpx b/reference/dusky.gpx
new file mode 100644 (file)
index 0000000..8cd9778
--- /dev/null
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gpx version="1.0" creator="GPSBabel - https://www.gpsbabel.org" xmlns="http://www.topografix.com/GPX/1/0">
+  <time>2022-12-28T08:23:44.990Z</time>
+  <bounds minlat="41.154633333" minlon="-85.165883333" maxlat="41.643716667" maxlon="-82.738933333"/>
+  <trk>
+    <name>dusky</name>
+    <trkseg>
+      <trkpt lat="41.383183333" lon="-82.738933333">
+        <ele>190.000</ele>
+        <time>2004-08-17T03:07:30.190Z</time>
+      </trkpt>
+      <trkpt lat="41.342766667" lon="-82.758350000">
+        <ele>217.000</ele>
+        <time>2004-08-17T03:10:55.190Z</time>
+      </trkpt>
+      <trkpt lat="41.343083333" lon="-82.764366667">
+        <ele>218.000</ele>
+        <time>2004-08-17T03:11:37.190Z</time>
+      </trkpt>
+      <trkpt lat="41.341883333" lon="-82.770550000">
+        <ele>218.000</ele>
+        <time>2004-08-17T03:12:00.200Z</time>
+      </trkpt>
+      <trkpt lat="41.347550000" lon="-82.806500000">
+        <ele>215.000</ele>
+        <time>2004-08-17T03:13:34.190Z</time>
+      </trkpt>
+      <trkpt lat="41.354783333" lon="-82.880766667">
+        <ele>201.000</ele>
+        <time>2004-08-17T03:16:45.200Z</time>
+      </trkpt>
+      <trkpt lat="41.410050000" lon="-83.141416667">
+        <ele>185.000</ele>
+        <time>2004-08-17T03:28:17.220Z</time>
+      </trkpt>
+      <trkpt lat="41.421083333" lon="-83.167333333">
+        <ele>186.000</ele>
+        <time>2004-08-17T03:29:33.220Z</time>
+      </trkpt>
+      <trkpt lat="41.453516667" lon="-83.273933333">
+        <ele>190.000</ele>
+        <time>2004-08-17T03:34:26.230Z</time>
+      </trkpt>
+      <trkpt lat="41.465083333" lon="-83.298083333">
+        <ele>188.000</ele>
+        <time>2004-08-17T03:35:39.230Z</time>
+      </trkpt>
+      <trkpt lat="41.473900000" lon="-83.332266667">
+        <ele>192.000</ele>
+        <time>2004-08-17T03:37:11.230Z</time>
+      </trkpt>
+      <trkpt lat="41.509716667" lon="-83.411850000">
+        <ele>195.000</ele>
+        <time>2004-08-17T03:41:07.240Z</time>
+      </trkpt>
+      <trkpt lat="41.513600000" lon="-83.430383333">
+        <ele>196.000</ele>
+        <time>2004-08-17T03:41:56.240Z</time>
+      </trkpt>
+      <trkpt lat="41.537833333" lon="-83.494216667">
+        <ele>195.000</ele>
+        <time>2004-08-17T03:44:58.240Z</time>
+      </trkpt>
+      <trkpt lat="41.541583333" lon="-83.519666667">
+        <ele>195.000</ele>
+        <time>2004-08-17T03:46:04.240Z</time>
+      </trkpt>
+      <trkpt lat="41.578583333" lon="-83.603633333">
+        <ele>196.000</ele>
+        <time>2004-08-17T03:50:12.250Z</time>
+      </trkpt>
+      <trkpt lat="41.583033333" lon="-83.611483333">
+        <ele>187.000</ele>
+        <time>2004-08-17T03:50:37.260Z</time>
+      </trkpt>
+      <trkpt lat="41.590650000" lon="-83.642066667">
+        <ele>199.000</ele>
+        <time>2004-08-17T03:51:59.250Z</time>
+      </trkpt>
+      <trkpt lat="41.595416667" lon="-83.782400000">
+        <ele>202.000</ele>
+        <time>2004-08-17T03:57:57.270Z</time>
+      </trkpt>
+      <trkpt lat="41.602716667" lon="-83.811050000">
+        <ele>213.000</ele>
+        <time>2004-08-17T03:59:14.280Z</time>
+      </trkpt>
+      <trkpt lat="41.598300000" lon="-84.012083333">
+        <ele>225.000</ele>
+        <time>2004-08-17T04:07:51.280Z</time>
+      </trkpt>
+      <trkpt lat="41.592716667" lon="-84.027233333">
+        <ele>233.000</ele>
+        <time>2004-08-17T04:08:34.280Z</time>
+      </trkpt>
+      <trkpt lat="41.591050000" lon="-84.325250000">
+        <ele>220.000</ele>
+        <time>2004-08-17T04:21:19.300Z</time>
+      </trkpt>
+      <trkpt lat="41.587600000" lon="-84.340950000">
+        <ele>221.000</ele>
+        <time>2004-08-17T04:22:00.300Z</time>
+      </trkpt>
+      <trkpt lat="41.603716667" lon="-84.391116667">
+        <ele>233.000</ele>
+        <time>2004-08-17T04:24:19.300Z</time>
+      </trkpt>
+      <trkpt lat="41.614016667" lon="-84.545983333">
+        <ele>275.000</ele>
+        <time>2004-08-17T04:30:59.320Z</time>
+      </trkpt>
+      <trkpt lat="41.615350000" lon="-84.548083333">
+        <ele>276.000</ele>
+        <time>2004-08-17T04:31:10.320Z</time>
+      </trkpt>
+      <trkpt lat="41.613066667" lon="-84.549383333">
+        <ele>276.000</ele>
+        <time>2004-08-17T04:31:29.320Z</time>
+      </trkpt>
+      <trkpt lat="41.613066667" lon="-84.553583333">
+        <ele>275.000</ele>
+        <time>2004-08-17T04:32:13.320Z</time>
+      </trkpt>
+      <trkpt lat="41.618583333" lon="-84.552900000">
+        <ele>269.000</ele>
+        <time>2004-08-17T04:32:46.330Z</time>
+      </trkpt>
+      <trkpt lat="41.639366667" lon="-84.553533333">
+        <ele>269.000</ele>
+        <time>2004-08-17T04:34:25.320Z</time>
+      </trkpt>
+      <trkpt lat="41.643450000" lon="-84.567883333">
+        <ele>267.000</ele>
+        <time>2004-08-17T04:35:21.320Z</time>
+      </trkpt>
+      <trkpt lat="41.643716667" lon="-84.573216667">
+        <ele>266.000</ele>
+        <time>2004-08-17T04:35:41.330Z</time>
+      </trkpt>
+      <trkpt lat="41.643333333" lon="-84.656733333">
+        <ele>281.000</ele>
+        <time>2004-08-17T04:40:11.330Z</time>
+      </trkpt>
+      <trkpt lat="41.641033333" lon="-84.666400000">
+        <ele>279.000</ele>
+        <time>2004-08-17T04:40:42.340Z</time>
+      </trkpt>
+      <trkpt lat="41.637316667" lon="-84.741400000">
+        <ele>298.000</ele>
+        <time>2004-08-17T04:45:08.340Z</time>
+      </trkpt>
+      <trkpt lat="41.635333333" lon="-84.751766667">
+        <ele>297.000</ele>
+        <time>2004-08-17T04:45:55.330Z</time>
+      </trkpt>
+      <trkpt lat="41.634566667" lon="-84.779566667">
+        <ele>306.000</ele>
+        <time>2004-08-17T04:47:39.340Z</time>
+      </trkpt>
+      <trkpt lat="41.630716667" lon="-84.814500000">
+        <ele>295.000</ele>
+        <time>2004-08-17T04:49:31.350Z</time>
+      </trkpt>
+      <trkpt lat="41.629400000" lon="-84.886300000">
+        <ele>330.000</ele>
+        <time>2004-08-17T04:52:58.340Z</time>
+      </trkpt>
+      <trkpt lat="41.632583333" lon="-84.904450000">
+        <ele>317.000</ele>
+        <time>2004-08-17T04:53:57.350Z</time>
+      </trkpt>
+      <trkpt lat="41.631566667" lon="-84.949500000">
+        <ele>314.000</ele>
+        <time>2004-08-17T04:56:20.360Z</time>
+      </trkpt>
+      <trkpt lat="41.634933333" lon="-84.995483333">
+        <ele>322.000</ele>
+        <time>2004-08-17T04:59:13.350Z</time>
+      </trkpt>
+      <trkpt lat="41.634916667" lon="-84.998933333">
+        <ele>332.000</ele>
+        <time>2004-08-17T04:59:34.370Z</time>
+      </trkpt>
+      <trkpt lat="41.634950000" lon="-85.049000000">
+        <ele>328.000</ele>
+        <time>2004-08-17T05:03:33.360Z</time>
+      </trkpt>
+      <trkpt lat="41.604600000" lon="-85.046950000">
+        <ele>304.000</ele>
+        <time>2004-08-17T05:05:34.380Z</time>
+      </trkpt>
+      <trkpt lat="41.598633333" lon="-85.049733333">
+        <ele>297.000</ele>
+        <time>2004-08-17T05:05:57.380Z</time>
+      </trkpt>
+      <trkpt lat="41.591533333" lon="-85.056850000">
+        <ele>296.000</ele>
+        <time>2004-08-17T05:06:29.370Z</time>
+      </trkpt>
+      <trkpt lat="41.584883333" lon="-85.059083333">
+        <ele>311.000</ele>
+        <time>2004-08-17T05:06:54.380Z</time>
+      </trkpt>
+      <trkpt lat="41.421783333" lon="-85.053633333">
+        <ele>285.000</ele>
+        <time>2004-08-17T05:16:40.390Z</time>
+      </trkpt>
+      <trkpt lat="41.354433333" lon="-85.087800000">
+        <ele>276.000</ele>
+        <time>2004-08-17T05:20:59.390Z</time>
+      </trkpt>
+      <trkpt lat="41.257883333" lon="-85.087266667">
+        <ele>275.000</ele>
+        <time>2004-08-17T05:26:41.400Z</time>
+      </trkpt>
+      <trkpt lat="41.248600000" lon="-85.091916667">
+        <ele>271.000</ele>
+        <time>2004-08-17T05:27:16.410Z</time>
+      </trkpt>
+      <trkpt lat="41.239800000" lon="-85.092766667">
+        <ele>266.000</ele>
+        <time>2004-08-17T05:27:47.410Z</time>
+      </trkpt>
+      <trkpt lat="41.232700000" lon="-85.096533333">
+        <ele>258.000</ele>
+        <time>2004-08-17T05:28:14.410Z</time>
+      </trkpt>
+      <trkpt lat="41.224600000" lon="-85.102716667">
+        <ele>265.000</ele>
+        <time>2004-08-17T05:28:47.410Z</time>
+      </trkpt>
+      <trkpt lat="41.216866667" lon="-85.104983333">
+        <ele>267.000</ele>
+        <time>2004-08-17T05:29:15.400Z</time>
+      </trkpt>
+      <trkpt lat="41.180600000" lon="-85.103883333">
+        <ele>258.000</ele>
+        <time>2004-08-17T05:31:24.410Z</time>
+      </trkpt>
+      <trkpt lat="41.179100000" lon="-85.104516667">
+        <ele>261.000</ele>
+        <time>2004-08-17T05:31:35.410Z</time>
+      </trkpt>
+      <trkpt lat="41.177683333" lon="-85.165883333">
+        <ele>264.000</ele>
+        <time>2004-08-17T05:36:07.410Z</time>
+      </trkpt>
+      <trkpt lat="41.155483333" lon="-85.165450000">
+        <ele>265.000</ele>
+        <time>2004-08-17T05:38:02.420Z</time>
+      </trkpt>
+      <trkpt lat="41.155250000" lon="-85.161616667">
+        <ele>262.000</ele>
+        <time>2004-08-17T05:38:28.410Z</time>
+      </trkpt>
+      <trkpt lat="41.154633333" lon="-85.158100000">
+        <ele>259.000</ele>
+        <time>2004-08-17T05:38:58.410Z</time>
+      </trkpt>
+    </trkseg>
+  </trk>
+</gpx>
index 91981bc48fa59feb7871d57b8445e33ad42f43bd..6d10d5cd002623a044d36dd3f850b75825c81b42 100644 (file)
@@ -36,9 +36,6 @@ html  html    HTML Output
 humminbird_ht  ht      Humminbird tracks (.ht)
 humminbird     hwr     Humminbird waypoints and routes (.hwr)
 lowranceusr    usr     Lowrance USR
-magellanx      upt     Magellan SD files (as for eXplorist)
-magellan               Magellan SD files (as for Meridian)
-magellan               Magellan serial protocol
 miniHomer              MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI)
 garmin_xt              Mobile Garmin XT Track files
 mtk-bin        bin     MTK Logger (iBlue 747,...) Binary File Format
index 70140cb00f559c841a4b3c5799e03b979efb362b..cc4d87520687c62ea6308e37e7b755400bf2688a 100644 (file)
@@ -42,9 +42,6 @@ file  humminbird_ht   ht      Humminbird tracks (.ht)
 file   humminbird      hwr     Humminbird waypoints and routes (.hwr)
 internal       random          Internal GPS data generator
 file   lowranceusr     usr     Lowrance USR
-file   magellanx       upt     Magellan SD files (as for eXplorist)
-file   magellan                Magellan SD files (as for Meridian)
-serial magellan                Magellan serial protocol
 serial miniHomer               MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI)
 file   garmin_xt               Mobile Garmin XT Track files
 file   mtk-bin bin     MTK Logger (iBlue 747,...) Binary File Format
index 70877eecb305e933839b3c9c31bad3ee2e0aeecd..7132352266702e29e6feed9c252caa926161d507 100644 (file)
@@ -42,9 +42,6 @@ file  r-rwr-  humminbird_ht   ht      Humminbird tracks (.ht)
 file   rwr-rw  humminbird      hwr     Humminbird waypoints and routes (.hwr)
 internal       r-r-r-  random          Internal GPS data generator
 file   rwrwrw  lowranceusr     usr     Lowrance USR
-file   rwrwrw  magellanx       upt     Magellan SD files (as for eXplorist)
-file   rwrwrw  magellan                Magellan SD files (as for Meridian)
-serial rwrwrw  magellan                Magellan serial protocol
 serial r-r---  miniHomer               MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI)
 file   --r---  garmin_xt               Mobile Garmin XT Track files
 file   r-r---  mtk-bin bin     MTK Logger (iBlue 747,...) Binary File Format
index f0f8b6081628ea1953be269578221caadc4dae5b..70596a81f4c79c9c7c8ad62742fe106c1c09a0ee 100644 (file)
@@ -508,30 +508,6 @@ option     lowranceusr     serialnum       (USR output) Device serial number       integer 0                       https
 
 option lowranceusr     description     (USR output) Output file content description    string                          https://www.gpsbabel.org/WEB_DOC_DIR/fmt_lowranceusr.html#fmt_lowranceusr_o_description
 
-file   rwrwrw  magellanx       upt     Magellan SD files (as for eXplorist)    magellanx
-       https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellanx.html
-option magellanx       deficon Default icon name       string                          https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellanx.html#fmt_magellanx_o_deficon
-
-option magellanx       maxcmts Max number of comments to write (maxcmts=200)   integer                         https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellanx.html#fmt_magellanx_o_maxcmts
-
-file   rwrwrw  magellan                Magellan SD files (as for Meridian)     magellan
-       https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html
-option magellan        deficon Default icon name       string                          https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_deficon
-
-option magellan        maxcmts Max number of comments to write (maxcmts=200)   integer                         https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_maxcmts
-
-serial rwrwrw  magellan                Magellan serial protocol        magellan
-       https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html
-option magellan        deficon Default icon name       string                          https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_deficon
-
-option magellan        maxcmts Max number of comments to write (maxcmts=200)   integer 200                     https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_maxcmts
-
-option magellan        baud    Numeric value of bitrate (baud=4800)    integer 4800                    https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_baud
-
-option magellan        noack   Suppress use of handshaking in name of speed    boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_noack
-
-option magellan        nukewpt Delete all waypoints    boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/fmt_magellan.html#fmt_magellan_o_nukewpt
-
 serial r-r---  miniHomer               MiniHomer, a skyTraq Venus 6 based logger (download tracks, waypoints and get/set POI)  miniHomer
        https://www.gpsbabel.org/WEB_DOC_DIR/fmt_miniHomer.html
 option miniHomer       baud    Baud rate used for download     integer 115200  0       115200  https://www.gpsbabel.org/WEB_DOC_DIR/fmt_miniHomer.html#fmt_miniHomer_o_baud
index f76efa9c09a74dfde973b850e1b16027eb5747ef..c787547d2d39aafb8be0e97b1088cbb15d18e5ab 100644 (file)
@@ -252,18 +252,6 @@ File Types (-i and -o options):
          title                 (USR output) Output file title string
          serialnum             (USR output) Device serial number
          description           (USR output) Output file content description
-       magellanx             Magellan SD files (as for eXplorist)
-         deficon               Default icon name
-         maxcmts               Max number of comments to write (maxcmts=200)
-       magellan              Magellan SD files (as for Meridian)
-         deficon               Default icon name
-         maxcmts               Max number of comments to write (maxcmts=200)
-       magellan              Magellan serial protocol
-         deficon               Default icon name
-         maxcmts               Max number of comments to write (maxcmts=200)
-         baud                  Numeric value of bitrate (baud=4800)
-         noack                 (0/1) Suppress use of handshaking in name of speed
-         nukewpt               (0/1) Delete all waypoints
        miniHomer             MiniHomer, a skyTraq Venus 6 based logger (downloa
          baud                  Baud rate used for download
          dump-file             Dump raw data to this file
diff --git a/reference/magfile b/reference/magfile
deleted file mode 100644 (file)
index 26216d1..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-$PMGNWPL,2805.200,N,08246.200,W,0000000,M,AL7394,872 6833 A TIDAL,a*43\r
-$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7485,872 6853 TIDAL 1,a*33\r
-$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7484,872 6853 TIDAL 2,a*31\r
-$PMGNWPL,2806.050,N,08246.367,W,0000000,M,AL7482,872 6853 TIDAL 4,a*34\r
-$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7471,872 6892 TIDAL 1,a*3A\r
-$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7470,872 6892 TIDAL 2,a*38\r
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7380,872 6899 TIDAL 1,a*35\r
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7379,872 6899 TIDAL 2,a*30\r
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7378,872 6899 TIDAL 3,a*30\r
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7377,872 6899 TIDAL 4,a*38\r
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7376,872 6899 TIDAL 5,a*38\r
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7386,872 6904 TIDAL 1,a*32\r
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7385,872 6904 TIDAL 2,a*32\r
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7384,872 6904 TIDAL 3,a*32\r
-$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7383,872 6904 TIDAL 4,a*34\r
-$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7382,872 6904 TIDAL 5,a*34\r
-$PMGNWPL,2809.617,N,08245.933,W,0000000,M,AL6625,872 6905 TIDAL 5,a*3B\r
-$PMGNWPL,2809.383,N,08244.666,W,0000000,M,AL7392,872 6906 TIDAL 2,a*31\r
diff --git a/reference/magxfile b/reference/magxfile
deleted file mode 100644 (file)
index f9ea738..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-$PMGNWPL,2805.200,N,08246.200,W,0000000,M,AL7394,872 6833 A TIDAL,a*43
-$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7485,872 6853 TIDAL 1,a*33
-$PMGNWPL,2806.216,N,08246.733,W,0000000,M,AL7484,872 6853 TIDAL 2,a*31
-$PMGNWPL,2806.050,N,08246.367,W,0000000,M,AL7482,872 6853 TIDAL 4,a*34
-$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7471,872 6892 TIDAL 1,a*3A
-$PMGNWPL,2808.816,N,08245.483,W,0000000,M,AL7470,872 6892 TIDAL 2,a*38
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7380,872 6899 TIDAL 1,a*35
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7379,872 6899 TIDAL 2,a*30
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7378,872 6899 TIDAL 3,a*30
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7377,872 6899 TIDAL 4,a*38
-$PMGNWPL,2809.017,N,08246.283,W,0000000,M,AL7376,872 6899 TIDAL 5,a*38
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7386,872 6904 TIDAL 1,a*32
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7385,872 6904 TIDAL 2,a*32
-$PMGNWPL,2809.216,N,08248.033,W,0000000,M,AL7384,872 6904 TIDAL 3,a*32
-$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7383,872 6904 TIDAL 4,a*34
-$PMGNWPL,2809.216,N,08248.017,W,0000000,M,AL7382,872 6904 TIDAL 5,a*34
-$PMGNWPL,2809.617,N,08245.933,W,0000000,M,AL6625,872 6905 TIDAL 5,a*3B
-$PMGNWPL,2809.383,N,08244.666,W,0000000,M,AL7392,872 6906 TIDAL 2,a*31
-$PMGNCMD,END*3D
diff --git a/reference/track/meridian.trk b/reference/track/meridian.trk
deleted file mode 100644 (file)
index abcb1a4..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-$PMGNTRK,3003.731,N,09136.621,W,00001,M,170621.25,A,,250502*6A\r
-$PMGNTRK,3003.767,N,09136.634,W,00000,M,170955.19,A,,250502*6F\r
-$PMGNTRK,3003.762,N,09136.496,W,00000,M,171200.20,A,,250502*60\r
-$PMGNTRK,3003.740,N,09136.443,W,00000,M,171248.75,A,,250502*64\r
-$PMGNTRK,3003.692,N,09136.317,W,00000,M,171441.20,A,,250502*63\r
-$PMGNTRK,3003.587,N,09135.964,W,00000,M,171716.20,A,,250502*68\r
-$PMGNTRK,3003.468,N,09135.801,W,00000,M,171746.20,A,,250502*6F\r
-$PMGNTRK,3003.323,N,09135.694,W,00000,M,171820.81,A,,250502*61\r
-$PMGNTRK,3003.233,N,09135.557,W,00000,M,171901.20,A,,250502*64\r
-$PMGNTRK,3002.984,N,09135.385,W,00000,M,172046.25,A,,250502*67\r
-$PMGNTRK,3002.941,N,09135.393,W,00000,M,172110.25,A,,250502*6B\r
-$PMGNTRK,3002.928,N,09135.576,W,00000,M,172151.37,A,,250502*6F\r
-$PMGNTRK,3002.774,N,09135.787,W,00000,M,172235.20,A,,250502*63\r
-$PMGNTRK,3002.731,N,09135.923,W,00000,M,172308.56,A,,250502*6C\r
-$PMGNTRK,3002.838,N,09136.016,W,00000,M,180423.93,A,,250502*6C\r
-$PMGNTRK,3002.820,N,09135.978,W,00002,M,180604.92,A,,250502*63\r
-$PMGNTRK,3002.786,N,09135.968,W,00000,M,180706.92,A,,250502*60\r
-$PMGNTRK,3002.772,N,09135.937,W,00001,M,180818.92,A,,250502*60\r
-$PMGNTRK,3002.782,N,09135.864,W,00000,M,181020.92,A,,250502*6B\r
-$PMGNTRK,3002.781,N,09135.830,W,00000,M,181109.93,A,,250502*62\r
-$PMGNTRK,3002.807,N,09135.780,W,00000,M,181218.92,A,,250502*65\r
-$PMGNTRK,3002.847,N,09135.712,W,00000,M,181422.93,A,,250502*64\r
-$PMGNTRK,3002.868,N,09135.686,W,00002,M,181504.93,A,,250502*62\r
-$PMGNTRK,3002.895,N,09135.645,W,00001,M,181614.93,A,,250502*6E\r
-$PMGNTRK,3002.921,N,09135.628,W,00001,M,181701.93,A,,250502*6E\r
-$PMGNTRK,3002.961,N,09135.631,W,00000,M,181807.94,A,,250502*6D\r
-$PMGNTRK,3003.019,N,09135.639,W,00002,M,181951.94,A,,250502*62\r
-$PMGNTRK,3003.047,N,09135.647,W,00000,M,182039.94,A,,250502*66\r
-$PMGNTRK,3003.074,N,09135.662,W,00000,M,182124.93,A,,250502*6B\r
-$PMGNTRK,3003.108,N,09135.662,W,00000,M,182217.94,A,,250502*65\r
-$PMGNTRK,3003.133,N,09135.680,W,00000,M,182318.93,A,,250502*68\r
-$PMGNTRK,3003.181,N,09135.681,W,00000,M,182437.94,A,,250502*6D\r
-$PMGNTRK,3003.292,N,09135.712,W,00006,M,182813.95,A,,250502*6A\r
-$PMGNTRK,3003.224,N,09135.696,W,00002,M,183136.94,A,,250502*60\r
-$PMGNTRK,3003.191,N,09135.687,W,00000,M,183256.95,A,,250502*6B\r
-$PMGNTRK,3003.158,N,09135.690,W,00000,M,183402.95,A,,250502*6F\r
-$PMGNTRK,3003.147,N,09135.726,W,00000,M,183603.95,A,,250502*6E\r
-$PMGNTRK,3003.149,N,09135.758,W,00000,M,183648.96,A,,250502*65\r
-$PMGNTRK,3003.159,N,09135.807,W,00001,M,183752.96,A,,250502*6A\r
-$PMGNTRK,3003.188,N,09135.871,W,00000,M,183918.95,A,,250502*65\r
-$PMGNTRK,3003.217,N,09135.878,W,00000,M,184015.96,A,,250502*69\r
-$PMGNTRK,3003.238,N,09135.866,W,00006,M,184125.96,A,,250502*6F\r
-$PMGNTRK,3003.217,N,09135.885,W,00000,M,184237.96,A,,250502*69\r
-$PMGNTRK,3003.192,N,09135.875,W,00000,M,184401.96,A,,250502*6B\r
-$PMGNTRK,3003.169,N,09135.851,W,00000,M,184553.96,A,,250502*6F\r
-$PMGNTRK,3003.154,N,09135.816,W,00000,M,184654.96,A,,250502*66\r
-$PMGNTRK,3003.140,N,09135.786,W,00000,M,184742.97,A,,250502*62\r
-$PMGNTRK,3003.135,N,09135.741,W,00000,M,184841.96,A,,250502*66\r
-$PMGNTRK,3003.133,N,09135.701,W,00000,M,184952.97,A,,250502*66\r
-$PMGNTRK,3003.113,N,09135.682,W,00000,M,185049.97,A,,250502*6C\r
-$PMGNTRK,3003.063,N,09135.664,W,00000,M,185214.97,A,,250502*68\r
-$PMGNTRK,3003.034,N,09135.654,W,00000,M,185256.98,A,,250502*60\r
-$PMGNTRK,3003.011,N,09135.646,W,00000,M,185338.98,A,,250502*6D\r
-$PMGNTRK,3002.946,N,09135.623,W,00000,M,185511.97,A,,250502*66\r
-$PMGNTRK,3002.907,N,09135.655,W,00000,M,185632.98,A,,250502*6F\r
-$PMGNTRK,3002.885,N,09135.685,W,00000,M,185724.97,A,,250502*60\r
-$PMGNTRK,3002.850,N,09135.727,W,00007,M,185840.98,A,,250502*64\r
-$PMGNTRK,3002.824,N,09135.760,W,00000,M,185928.98,A,,250502*6C\r
-$PMGNTRK,3002.798,N,09135.796,W,00000,M,190022.98,A,,250502*6A\r
-$PMGNTRK,3002.784,N,09135.859,W,00000,M,190141.98,A,,250502*6F\r
-$PMGNTRK,3002.774,N,09135.908,W,00000,M,190248.99,A,,250502*6E\r
-$PMGNTRK,3002.779,N,09135.938,W,00000,M,190343.98,A,,250502*6B\r
-$PMGNTRK,3002.807,N,09135.957,W,00000,M,190449.99,A,,250502*68\r
-$PMGNTRK,3002.828,N,09135.980,W,00000,M,190557.98,A,,250502*60\r
diff --git a/testo.d/classic-1.test b/testo.d/classic-1.test
deleted file mode 100644 (file)
index 1eb1ea0..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/sh
-
-
-# Magellan serial
-# TODO
-
-
-#
-# GPX routes -- since GPX contains a date stamp, tests will always
-# fail, so we use magellan as an interim format...
-#
-rm -f ${TMPDIR}/gpxroute.gpx ${TMPDIR}/maggpx.rte
-gpsbabel -r -i gpx -f ${REFERENCE}/route/route.gpx -o gpx \
-         -F ${TMPDIR}/gpxroute.gpx
-gpsbabel -r -i gpx -f ${TMPDIR}/gpxroute.gpx -o magellan \
-         -F ${TMPDIR}/maggpx.rte
-compare ${REFERENCE}/route/magellan.rte ${TMPDIR}/maggpx.rte
-
-#
-# GPX tracks -- since GPX contains a date stamp, tests will always
-# fail, so we use magellan as an interim format...
-#
-rm -f ${TMPDIR}/gpxtrack.gpx ${TMPDIR}/maggpx.trk
-gpsbabel -t -i gpx -f ${REFERENCE}/track/tracks.gpx -o gpx \
-         -F ${TMPDIR}/gpxtrack.gpx
-gpsbabel -t -i magellan -f ${REFERENCE}/track/meridian.trk -o gpx \
-         -F ${TMPDIR}/maggpx.trk
-compare ${TMPDIR}/maggpx.trk ${TMPDIR}/gpxtrack.gpx
-
index 8bcaf9115a02d007c0592bd3e9f5df1bc495aa57..96f5fc9d783e82dddef8985b86b39afdcf543bc7 100644 (file)
@@ -12,7 +12,7 @@ compare ${REFERENCE}/humanread.out ${TMPDIR}/humanread.out
 # XCSV "path distance" test
 #
 rm -f ${TMPDIR}/pathdist.out
-gpsbabel -i magellan -f ${REFERENCE}/dusky.trk -o xcsv,style=${REFERENCE}/gnuplot.style -F ${TMPDIR}/pathdist.out
+gpsbabel -i gpx -f ${REFERENCE}/dusky.gpx -o xcsv,style=${REFERENCE}/gnuplot.style -F ${TMPDIR}/pathdist.out
 compare ${REFERENCE}/dusky.gnuplot ${TMPDIR}/pathdist.out
 
 #
diff --git a/testo.d/magellan.test b/testo.d/magellan.test
deleted file mode 100644 (file)
index f6f4e80..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-#
-# Magellan file format
-#
-gpsbabel -i magellan -f ${REFERENCE}/magfile -o magellan -F ${TMPDIR}/magfile
-compare ${TMPDIR}/magfile ${REFERENCE}/magfile
-
-#
-# Magellanx is just like, but with longer names. (which this admittedly
-# doesn't actually exercise...)
-#
-gpsbabel -i magellan -f ${REFERENCE}/magxfile -o magellanx -F ${TMPDIR}/magxfile
-compare ${TMPDIR}/magxfile ${REFERENCE}/magxfile
-
-# Magellanx routes, however, have an extra 'name' field in them.
-gpsbabel -r -i magellanx -f ${REFERENCE}/route/magexplorist.rte -o magellanx -F ${TMPDIR}/magxfile.rte
-gpsbabel -r -i magellanx -f ${TMPDIR}/magxfile.rte -o magellanx -F ${TMPDIR}/magxfile2.rte
-compare ${REFERENCE}/route/magexplorist.rte ${TMPDIR}/magxfile2.rte
-
-gpsbabel -t -i gpx -f ${REFERENCE}/track/nmeadate.gpx -o magellanx -F ${TMPDIR}/magellandate.log
-compare ${REFERENCE}/track/magellandate.log ${TMPDIR}/magellandate.log
diff --git a/testo.d/magellan_sd.test b/testo.d/magellan_sd.test
deleted file mode 100644 (file)
index db472d5..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# magellan SD card waypoint / route format
-#
-rm -f ${TMPDIR}/magellan.rte
-gpsbabel -r -i magellan -f ${REFERENCE}/route/magellan.rte -o magellan \
-         -F ${TMPDIR}/magellan.rte
-compare ${REFERENCE}/route/magellan.rte ${TMPDIR}/magellan.rte
-
diff --git a/vecs.cc b/vecs.cc
index 0df9ee6412645743e94ca2e4f31861701a6ebf49..45f0c1e90fef948ce57076b428f32b5bb4e2c454 100644 (file)
--- a/vecs.cc
+++ b/vecs.cc
@@ -69,9 +69,6 @@
 
 
 extern ff_vecs_t geo_vecs;
-extern ff_vecs_t mag_svecs;
-extern ff_vecs_t mag_fvecs;
-extern ff_vecs_t magX_fvecs;
 extern ff_vecs_t garmin_vecs;
 extern ff_vecs_t ozi_vecs;
 #if MAXIMAL_ENABLED
@@ -120,9 +117,6 @@ struct Vecs::Impl {
 #endif // CSVFMTS_ENABLED
   LegacyFormat geo_fmt {geo_vecs};
   GpxFormat gpx_fmt;
-  LegacyFormat mag_sfmt {mag_svecs};
-  LegacyFormat mag_ffmt {mag_fvecs};
-  LegacyFormat magX_ffmt {magX_fvecs};
   LegacyFormat garmin_fmt {garmin_vecs};
   GdbFormat gdb_fmt;
   NmeaFormat nmea_fmt;
@@ -208,27 +202,6 @@ struct Vecs::Impl {
       "gpx",
       nullptr,
     },
-    {
-      &mag_sfmt,
-      "magellan",
-      "Magellan serial protocol",
-      nullptr,
-      nullptr,
-    },
-    {
-      &mag_ffmt,
-      "magellan",
-      "Magellan SD files (as for Meridian)",
-      nullptr,
-      nullptr,
-    },
-    {
-      &magX_ffmt,
-      "magellanx",
-      "Magellan SD files (as for eXplorist)",
-      "upt",
-      nullptr,
-    },
     {
       &garmin_fmt,
       "garmin",
index 339b14e292cf3e30a77f97222339e509be4e6288..61e0d64543b95acebbef094e9b1d6281e2431b42 100644 (file)
@@ -12,7 +12,7 @@ standard in XML to contain all the data, but there are too many
 programs that don't understand it yet and too much data in
 alternate formats.</para>
       <para>Perhaps you have an
-         <link linkend="fmt_magellanx">Explorist 600</link>
+         <link linkend="fmt_garmin">GPSMap 60CSx</link>
          and your friend has a
          <link linkend="fmt_garmin">StreetPilot 2720</link>
          .
index 4431ba8eb4ab13e696e7ac0c8f7ebffb0c2b0756..1aa0a99babbb607dbddb36520224007b95786ed5 100644 (file)
@@ -207,8 +207,6 @@ merged data to multiple destinations.
          transparently with waypoints, tracks, and routes.  Some
          formats, like
       <link linkend="fmt_garmin">garmin</link>
-      and
-      <link linkend="fmt_magellan">magellan</link>
       require the
       <parameter>-t</parameter>
       flag to work with tracks and
diff --git a/xmldoc/formats/magellan.xml b/xmldoc/formats/magellan.xml
deleted file mode 100644 (file)
index 9a25f8e..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<para>GPSBabel supports the following <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.magellangps.com">Magellan</link> receivers:
-<simplelist columns="2" type="vert">
-       <member>310</member>
-       <member>315</member>
-       <member>Map330</member>
-       <member>SporTrak Map Color</member>
-       <member>SporTrak Map</member>
-       <member>SporTrak Map Pro</member>
-       <member>SporTrak Map Topo</member>
-       <member>Meridian (green or yellow)</member>
-       <member>Meridian Gold</member>
-       <member>Meridian Platinum</member>
-       <member>Meridian Color</member>
-       <member>Explorist 100 (with aftermarket cable)</member>
-       <member>Explorist 200 (with aftermarket cable)</member>
-       <member>Explorist 300 (with aftermarket cable)</member>
-       <member>Explorist 210</member>
-       <member>Explorist 300</member>
-       <member>Explorist 400</member>
-       <member>Explorist 500</member>
-       <member>Explorist 600</member>
-       <member>Explorist XL</member>
-</simplelist>
-</para>
-
-<para>
-       This format is used for both the serial protocol used on the
-       devices with serial ports such as Map330 and Meridian and for
-       the files stored either in either the unit's internal memory
-       (Explorist 210, Explorist 400, Explorist 500, Explorist 600,
-       Explorist XL) or on removable memory.
-</para>
-<para>
-       If you specify a serial port for the file (.e.g. "COM1", "/dev/ttyS0")
-       to be read or written, GPSBabel will use serial protocol.   Specifying
-       a file, either on local filesystem or on a mounted flash card reader,
-       will results in the file-based format being used.
-</para>
diff --git a/xmldoc/formats/magellan1.xml b/xmldoc/formats/magellan1.xml
deleted file mode 100644 (file)
index e393664..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-<para>GPSBabel supports the following <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.magellangps.com">Magellan</link> receivers:
-<simplelist columns="2" type="vert">
-       <member>310</member>
-       <member>315</member>
-       <member>Map330</member>
-       <member>SporTrak Map Color</member>
-       <member>SporTrak Map</member>
-       <member>SporTrak Map Pro</member>
-       <member>SporTrak Map Topo</member>
-       <member>Meridian (green or yellow)</member>
-       <member>Meridian Gold</member>
-       <member>Meridian Platinum</member>
-       <member>Meridian Color</member>
-       <member>Explorist 100 (with aftermarket cable)</member>
-       <member>Explorist 200 (with aftermarket cable)</member>
-       <member>Explorist 300 (with aftermarket cable)</member>
-       <member>Explorist 210</member>
-       <member>Explorist 300</member>
-       <member>Explorist 400</member>
-       <member>Explorist 500</member>
-       <member>Explorist 600</member>
-       <member>Explorist XL</member>
-</simplelist>
-</para>
-<para>
-The RoadMate family of products is not supported.
-</para>
-
-<para>
-       This format is used for both the serial protocol used on the
-       devices with serial ports such as Map330 and Meridian and for
-       the files stored either in either the unit's internal memory
-       (Explorist 210, 400, 500, 600, XL) or on removable memory.
-</para>
-<para>
-       If you specify a serial port for the file (.e.g. "COM1", "/dev/ttyS0")
-       to be read or written, GPSBabel will use serial protocol.   Specifying
-       a file, either on local filesystem or on a mounted flash card reader,
-       will result in the file-based format being used.
-</para>
-<para>
-       Users of the Explorist generation of receivers should probably
-       prefer to use the <link linkend="fmt_magellan">magellanx</link>
-       format over this one.
-
-</para>
-<important>
-<para>
-This module does not support the units that do not follow Magellan's
-documented communications protocols including:</para>
-<simplelist columns="2" type="vert">
-<member>Maestro 3100</member>
-<member>Maestro 3140</member>
-<member>Maestro 3200</member>
-<member>Maestro 3210</member>
-<member>Maestro 3220</member>
-<member>Maestro 3225</member>
-<member>Maestro 3250</member>
-<member>Maestro 4000</member>
-<member>Maestro 4040</member>
-<member>Maestro 4050</member>
-<member>Maestro 4200</member>
-<member>Maestro 4210</member>
-<member>Maestro 4220</member>
-<member>Maestro 4250</member>
-<member>Maestro 5310</member>
-
-<member> RoadMate 300 </member>
-<member> RoadMate 360 </member>
-<member> RoadMate 500</member>
-<member> RoadMate 700 </member>
-<member> RoadMate 760</member>
-<member> RoadMate 800</member>
-<member> RoadMate 860T</member>
-<member> RoadMate 1200 </member>
-<member> RoadMate 1400 </member>
-<member> RoadMate 1412 </member>
-<member> RoadMate 1430 </member>
-<member> RoadMate 2000 </member>
-<member> RoadMate 2000 </member>
-<member> RoadMate 2200T</member>
-<member> RoadMate 3000T</member>
-<member> RoadMate 3050T</member>
-<member> RoadMate 6000T</member>
-<member> RoadMate AAA </member>
-
-<member> Triton 200 </member>
-<member> Triton 300 </member>
-<member> Triton 400 </member>
-<member> Triton 500 </member>
-<member> Triton 1500 </member>
-<member> Triton 2000 </member>
-
-</simplelist>
-
-</important>
diff --git a/xmldoc/formats/magellanx.xml b/xmldoc/formats/magellanx.xml
deleted file mode 100644 (file)
index ff07420..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<para>
-       This is the SD card format used by the <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.magellangps.com">Magellan</link> Explorist 400,
-       Explorist 500, Explorist 600, and Explorist XL and internally on those devices plus the
-       Explorist 210.  Stored waypoints are identical to the <link linkend="fmt_magellan">Magellan SD format</link>
-       used by Meridian, but the newer models allow longer waypoint names.  Routes are
-       subtly different.
-</para>
-<para>
-       You should name any file containing waypoints created with
-       this format with a ".upt" extension so the firmware can read it.
-       Similarly, routes should be named ".rte" and tracks should be
-       named ".log".
-</para>
diff --git a/xmldoc/formats/options/magellan-deficon.xml b/xmldoc/formats/options/magellan-deficon.xml
deleted file mode 100644 (file)
index 50c507e..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<para>
-  The deficon option is used to control the icon output when writing to this format.   It overrides any icon information that might be present in the source data.
-</para>
diff --git a/xmldoc/formats/options/magellan-maxcmts.xml b/xmldoc/formats/options/magellan-maxcmts.xml
deleted file mode 100644 (file)
index 70104bb..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<para>
-The maxcmts option allows you to specify the number comments that will
-be sent to the unit.
-</para>
-<para>
-Magellan receivers allow a maximum of 200 waypoint comments.  Unfortunately,
-DirectRoute uses waypoint comments to provide next turn directions for
-navigation pop-ups and that comes from that pool of 200 comments.  It
-is therefore sometimes convenient to limit the number of waypoint
-comments written to the receiver.   For example, a geocacher might want
-to upload 400 waypoints, but only 190 with comments so that DirectRoute
-could provide driving directions for the next ten turns.
-</para>
diff --git a/xmldoc/formats/options/magellan1-baud.xml b/xmldoc/formats/options/magellan1-baud.xml
deleted file mode 100644 (file)
index c6370d6..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<para>
-  This option causes GPSBabel to use the given baud rate for serial
-  communications.  It must match the given baud rate on the receiver.  The
-  default value matches the default on the receiver, 4800.
-</para>
-<para>
-  Valid options are 1200, 2400, 4800, 9600, 19200, 57600, and 115200.
-</para>
diff --git a/xmldoc/formats/options/magellan1-deficon.xml b/xmldoc/formats/options/magellan1-deficon.xml
deleted file mode 100644 (file)
index f1d272b..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<para>
-This option specifies the icon or waypoint type to write for each waypoint on
-output.
-</para>
-<para>
-If this option is specified, its value will be used for all waypoints, not
-just those that do not already have descriptions.  That is, this option
-overrides any icon description that might be in the input file.
-</para>
-<!--
-Eventually link to a table of valid icons...
--->
-<para>
-This option has no effect on input.
-</para>
diff --git a/xmldoc/formats/options/magellan1-maxcmts.xml b/xmldoc/formats/options/magellan1-maxcmts.xml
deleted file mode 100644 (file)
index 70104bb..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<para>
-The maxcmts option allows you to specify the number comments that will
-be sent to the unit.
-</para>
-<para>
-Magellan receivers allow a maximum of 200 waypoint comments.  Unfortunately,
-DirectRoute uses waypoint comments to provide next turn directions for
-navigation pop-ups and that comes from that pool of 200 comments.  It
-is therefore sometimes convenient to limit the number of waypoint
-comments written to the receiver.   For example, a geocacher might want
-to upload 400 waypoints, but only 190 with comments so that DirectRoute
-could provide driving directions for the next ten turns.
-</para>
diff --git a/xmldoc/formats/options/magellan1-noack.xml b/xmldoc/formats/options/magellan1-noack.xml
deleted file mode 100644 (file)
index 8727166..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<para>
-Magellan's protocol specification strongly encourages the use of software
-acknowledgments on every packets.   This is a simple "this is what I think
-I heard.  If you agree that I heard it correctly, let's go to the next packet"
-handshake that is used to ensure the integrity of the data transfer.
-</para>
-<para>
-Certain firmware versions have problems handling this which makes transfers
-unnecessarily slow.  Transfers on all units at high serial speeds  are also
-severely restricted by this process.
-</para>
-<para>
-In controlled environments (good cables, low electrical noise, receiving
-from the unit, not doing donuts with the unit set to "track up" at a 150
-mile scale with 500 waypoints on the screen) it is sometimes useful to
-release that safety belt by using the "noack" suboption.
-</para>
diff --git a/xmldoc/formats/options/magellan1-nukewpt.xml b/xmldoc/formats/options/magellan1-nukewpt.xml
deleted file mode 100644 (file)
index 57de6cc..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<para>
-This option erases all waypoints in the receiver before doing a transfer.
-</para>
-<para>
-This is a convenient option to use in automated processes where you want
-to ensure the units starts with a clean state before sending waypoints to
-it.   Using this option on transmit is a better idea than doing it on
-receive since the latter would erase all the waypoints before asking the
-unit to send all the waypoints.
-</para>
diff --git a/xmldoc/formats/options/magellanx-deficon.xml b/xmldoc/formats/options/magellanx-deficon.xml
deleted file mode 100644 (file)
index 50c507e..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<para>
-  The deficon option is used to control the icon output when writing to this format.   It overrides any icon information that might be present in the source data.
-</para>
diff --git a/xmldoc/formats/options/magellanx-maxcmts.xml b/xmldoc/formats/options/magellanx-maxcmts.xml
deleted file mode 100644 (file)
index 70104bb..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<para>
-The maxcmts option allows you to specify the number comments that will
-be sent to the unit.
-</para>
-<para>
-Magellan receivers allow a maximum of 200 waypoint comments.  Unfortunately,
-DirectRoute uses waypoint comments to provide next turn directions for
-navigation pop-ups and that comes from that pool of 200 comments.  It
-is therefore sometimes convenient to limit the number of waypoint
-comments written to the receiver.   For example, a geocacher might want
-to upload 400 waypoints, but only 190 with comments so that DirectRoute
-could provide driving directions for the next ten turns.
-</para>